Commit 2c643a55 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into notebooklab-in-repo

parents bf5248ac d7a52716
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-git-2.7-phantomjs-2.1-node-7.1" image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1"
cache: cache:
key: "ruby-233" key: "ruby-233"
......
...@@ -25,14 +25,20 @@ logs, and code as it's very hard to read otherwise.) ...@@ -25,14 +25,20 @@ logs, and code as it's very hard to read otherwise.)
#### Results of GitLab environment info #### Results of GitLab environment info
<details>
(For installations with omnibus-gitlab package run and paste the output of: (For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`) `sudo gitlab-rake gitlab:env:info`)
(For installations from source run and paste the output of: (For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`) `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
</details>
#### Results of GitLab application Check #### Results of GitLab application Check
<details>
(For installations with omnibus-gitlab package run and paste the output of: (For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:check SANITIZE=true`) `sudo gitlab-rake gitlab:check SANITIZE=true`)
...@@ -41,6 +47,8 @@ logs, and code as it's very hard to read otherwise.) ...@@ -41,6 +47,8 @@ logs, and code as it's very hard to read otherwise.)
(we will only investigate if the tests are passing) (we will only investigate if the tests are passing)
</details>
### Possible fixes ### Possible fixes
(If you can, link to the line of code that might be responsible for the problem) (If you can, link to the line of code that might be responsible for the problem)
...@@ -330,7 +330,7 @@ GEM ...@@ -330,7 +330,7 @@ GEM
grape-entity (0.6.0) grape-entity (0.6.0)
activesupport activesupport
multi_json (>= 1.3.2) multi_json (>= 1.3.2)
grpc (1.1.2) grpc (1.2.2)
google-protobuf (~> 3.1) google-protobuf (~> 3.1)
googleauth (~> 0.5.1) googleauth (~> 0.5.1)
haml (4.0.7) haml (4.0.7)
......
...@@ -50,7 +50,7 @@ window.gl.GfmAutoComplete = { ...@@ -50,7 +50,7 @@ window.gl.GfmAutoComplete = {
template: '<li>${title}</li>' template: '<li>${title}</li>'
}, },
Loading: { Loading: {
template: '<li style="pointer-events: none;"><i class="fa fa-refresh fa-spin"></i> Loading...</li>' template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>'
}, },
DefaultOptions: { DefaultOptions: {
sorter: function(query, items, searchKey) { sorter: function(query, items, searchKey) {
......
...@@ -20,57 +20,60 @@ class Issue { ...@@ -20,57 +20,60 @@ class Issue {
}); });
Issue.initIssueBtnEventListeners(); Issue.initIssueBtnEventListeners();
} }
Issue.$btnNewBranch = $('#new-branch');
Issue.initMergeRequests(); Issue.initMergeRequests();
Issue.initRelatedBranches(); Issue.initRelatedBranches();
Issue.initCanCreateBranch(); Issue.initCanCreateBranch();
} }
static initIssueBtnEventListeners() { static initIssueBtnEventListeners() {
var issueFailMessage; const issueFailMessage = 'Unable to update this issue at this time.';
issueFailMessage = 'Unable to update this issue at this time.';
return $('a.btn-close, a.btn-reopen').on('click', function(e) { const closeButtons = $('a.btn-close');
var $this, isClose, shouldSubmit, url; const isClosedBadge = $('div.status-box-closed');
const isOpenBadge = $('div.status-box-open');
const projectIssuesCounter = $('.issue_counter');
const reopenButtons = $('a.btn-reopen');
return closeButtons.add(reopenButtons).on('click', function(e) {
var $this, shouldSubmit, url;
e.preventDefault(); e.preventDefault();
e.stopImmediatePropagation(); e.stopImmediatePropagation();
$this = $(this); $this = $(this);
isClose = $this.hasClass('btn-close');
shouldSubmit = $this.hasClass('btn-comment'); shouldSubmit = $this.hasClass('btn-comment');
if (shouldSubmit) { if (shouldSubmit) {
Issue.submitNoteForm($this.closest('form')); Issue.submitNoteForm($this.closest('form'));
} }
$this.prop('disabled', true); $this.prop('disabled', true);
Issue.setNewBranchButtonState(true, null);
url = $this.attr('href'); url = $this.attr('href');
return $.ajax({ return $.ajax({
type: 'PUT', type: 'PUT',
url: url, url: url
error: function(jqXHR, textStatus, errorThrown) { }).fail(function(jqXHR, textStatus, errorThrown) {
var issueStatus; new Flash(issueFailMessage);
issueStatus = isClose ? 'close' : 'open'; Issue.initCanCreateBranch();
return new Flash(issueFailMessage, 'alert'); }).done(function(data, textStatus, jqXHR) {
}, if ('id' in data) {
success: function(data, textStatus, jqXHR) { $(document).trigger('issuable:change');
if ('id' in data) {
$(document).trigger('issuable:change'); const isClosed = $this.hasClass('btn-close');
let total = Number($('.issue_counter').text().replace(/[^\d]/, '')); closeButtons.toggleClass('hidden', isClosed);
if (isClose) { reopenButtons.toggleClass('hidden', !isClosed);
$('a.btn-close').addClass('hidden'); isClosedBadge.toggleClass('hidden', !isClosed);
$('a.btn-reopen').removeClass('hidden'); isOpenBadge.toggleClass('hidden', isClosed);
$('div.status-box-closed').removeClass('hidden');
$('div.status-box-open').addClass('hidden'); let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, ''));
total -= 1; numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
} else { projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
$('a.btn-reopen').addClass('hidden'); } else {
$('a.btn-close').removeClass('hidden'); new Flash(issueFailMessage);
$('div.status-box-closed').addClass('hidden');
$('div.status-box-open').removeClass('hidden');
total += 1;
}
$('.issue_counter').text(gl.text.addDelimiter(total));
} else {
new Flash(issueFailMessage, 'alert');
}
return $this.prop('disabled', false);
} }
$this.prop('disabled', false);
Issue.initCanCreateBranch();
}); });
}); });
} }
...@@ -86,9 +89,9 @@ class Issue { ...@@ -86,9 +89,9 @@ class Issue {
static initMergeRequests() { static initMergeRequests() {
var $container; var $container;
$container = $('#merge-requests'); $container = $('#merge-requests');
return $.getJSON($container.data('url')).error(function() { return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load referenced merge requests', 'alert'); return new Flash('Failed to load referenced merge requests');
}).success(function(data) { }).done(function(data) {
if ('html' in data) { if ('html' in data) {
return $container.html(data.html); return $container.html(data.html);
} }
...@@ -98,9 +101,9 @@ class Issue { ...@@ -98,9 +101,9 @@ class Issue {
static initRelatedBranches() { static initRelatedBranches() {
var $container; var $container;
$container = $('#related-branches'); $container = $('#related-branches');
return $.getJSON($container.data('url')).error(function() { return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load related branches', 'alert'); return new Flash('Failed to load related branches');
}).success(function(data) { }).done(function(data) {
if ('html' in data) { if ('html' in data) {
return $container.html(data.html); return $container.html(data.html);
} }
...@@ -108,24 +111,27 @@ class Issue { ...@@ -108,24 +111,27 @@ class Issue {
} }
static initCanCreateBranch() { static initCanCreateBranch() {
var $container;
$container = $('#new-branch');
// If the user doesn't have the required permissions the container isn't // If the user doesn't have the required permissions the container isn't
// rendered at all. // rendered at all.
if ($container.length === 0) { if (Issue.$btnNewBranch.length === 0) {
return; return;
} }
return $.getJSON($container.data('path')).error(function() { return $.getJSON(Issue.$btnNewBranch.data('path')).fail(function() {
$container.find('.unavailable').show(); Issue.setNewBranchButtonState(false, false);
return new Flash('Failed to check if a new branch can be created.', 'alert'); new Flash('Failed to check if a new branch can be created.');
}).success(function(data) { }).done(function(data) {
if (data.can_create_branch) { Issue.setNewBranchButtonState(false, data.can_create_branch);
$container.find('.available').show();
} else {
return $container.find('.unavailable').show();
}
}); });
} }
static setNewBranchButtonState(isPending, canCreate) {
if (Issue.$btnNewBranch.length === 0) {
return;
}
Issue.$btnNewBranch.find('.available').toggle(!isPending && canCreate);
Issue.$btnNewBranch.find('.unavailable').toggle(!isPending && !canCreate);
}
} }
export default Issue; export default Issue;
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
direction: rtl; direction: rtl;
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
overflow-x: scroll; overflow-x: auto;
} }
} }
......
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
.input-token:last-child { .input-token:last-child {
flex: 1; flex: 1;
-webkit-flex: 1; -webkit-flex: 1;
max-width: initial; max-width: inherit;
} }
} }
...@@ -246,17 +246,17 @@ ...@@ -246,17 +246,17 @@
} }
} }
.filtered-search-history-dropdown-toggle-button { .filtered-search-history-dropdown-wrapper {
position: static;
display: flex; display: flex;
align-items: center; flex-direction: column;
}
.filtered-search-history-dropdown-toggle-button {
flex: 1;
width: auto; width: auto;
height: 100%; padding-right: 10px;
padding-top: 0;
padding-left: 0.75em;
padding-bottom: 0;
padding-right: 0.5em;
background-color: transparent;
border-radius: 0; border-radius: 0;
border-top: 0; border-top: 0;
border-left: 0; border-left: 0;
...@@ -264,6 +264,7 @@ ...@@ -264,6 +264,7 @@
border-right: 1px solid $border-color; border-right: 1px solid $border-color;
color: $gl-text-color-secondary; color: $gl-text-color-secondary;
line-height: 1;
transition: color 0.1s linear; transition: color 0.1s linear;
...@@ -275,24 +276,21 @@ ...@@ -275,24 +276,21 @@
} }
.dropdown-toggle-text { .dropdown-toggle-text {
display: inline-block;
color: inherit; color: inherit;
.fa { .fa {
vertical-align: middle;
color: inherit; color: inherit;
} }
} }
.fa { .fa {
position: initial; position: static;
} }
} }
.filtered-search-history-dropdown-wrapper {
position: initial;
flex-shrink: 0;
}
.filtered-search-history-dropdown { .filtered-search-history-dropdown {
width: 40%; width: 40%;
......
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
overflow-y: hidden; overflow-y: hidden;
font-size: 12px; font-size: 12px;
.fa-refresh { .fa-spinner {
font-size: 24px; font-size: 24px;
margin-left: 20px; margin-left: 20px;
} }
...@@ -219,7 +219,7 @@ ...@@ -219,7 +219,7 @@
font-size: 12px; font-size: 12px;
position: relative; position: relative;
.fa-refresh { .fa-spinner {
font-size: 24px; font-size: 24px;
} }
...@@ -366,7 +366,7 @@ ...@@ -366,7 +366,7 @@
background-color: $row-hover; background-color: $row-hover;
} }
.fa-refresh { .fa-spinner {
font-size: 13px; font-size: 13px;
margin-left: 3px; margin-left: 3px;
} }
......
...@@ -10,10 +10,14 @@ ...@@ -10,10 +10,14 @@
position: relative; position: relative;
&.event-inline { &.event-inline {
.profile-icon { .system-note-image {
top: 20px; top: 20px;
} }
.user-avatar {
top: 14px;
}
.event-title, .event-title,
.event-item-timestamp { .event-item-timestamp {
line-height: 40px; line-height: 40px;
...@@ -24,7 +28,7 @@ ...@@ -24,7 +28,7 @@
color: $gl-text-color; color: $gl-text-color;
} }
.profile-icon { .system-note-image {
position: absolute; position: absolute;
left: 0; left: 0;
top: 14px; top: 14px;
...@@ -35,15 +39,18 @@ ...@@ -35,15 +39,18 @@
fill: $gl-text-color-secondary; fill: $gl-text-color-secondary;
} }
&.open-icon svg { &.opened-icon,
fill: $green-300; &.created-icon {
svg {
fill: $green-300;
}
} }
&.closed-icon svg { &.closed-icon svg {
fill: $red-300; fill: $red-300;
} }
&.fork-icon svg { &.accepted-icon svg {
fill: $blue-300; fill: $blue-300;
} }
} }
...@@ -128,8 +135,7 @@ ...@@ -128,8 +135,7 @@
li { li {
&.commit { &.commit {
background: transparent; background: transparent;
padding: 3px; padding: 0;
padding-left: 0;
border: none; border: none;
.commit-row-title { .commit-row-title {
...@@ -183,7 +189,7 @@ ...@@ -183,7 +189,7 @@
max-width: 100%; max-width: 100%;
} }
.profile-icon { .system-note-image {
display: none; display: none;
} }
......
...@@ -527,6 +527,8 @@ ...@@ -527,6 +527,8 @@
} }
.comments-disabled-notif { .comments-disabled-notif {
line-height: 28px;
.btn { .btn {
margin-left: 5px; margin-left: 5px;
} }
......
...@@ -18,12 +18,12 @@ ul.notes { ...@@ -18,12 +18,12 @@ ul.notes {
float: left; float: left;
svg { svg {
width: 18px; width: 16px;
height: 18px; height: 16px;
fill: $gray-darkest; fill: $gray-darkest;
position: absolute; position: absolute;
left: 30px; left: 0;
top: 15px; top: 16px;
} }
} }
...@@ -144,6 +144,10 @@ ul.notes { ...@@ -144,6 +144,10 @@ ul.notes {
padding: 0; padding: 0;
clear: both; clear: both;
@media (min-width: $screen-sm-min) {
margin-left: 65px;
}
&.timeline-entry::after { &.timeline-entry::after {
clear: none; clear: none;
} }
...@@ -172,6 +176,10 @@ ul.notes { ...@@ -172,6 +176,10 @@ ul.notes {
.timeline-content { .timeline-content {
padding: 14px 10px; padding: 14px 10px;
@media (min-width: $screen-sm-min) {
margin-left: 20px;
}
} }
.note-header { .note-header {
......
...@@ -61,7 +61,6 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -61,7 +61,6 @@ class Projects::CompareController < Projects::ApplicationController
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last @environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
@diff_notes_disabled = true @diff_notes_disabled = true
@grouped_diff_discussions = {}
end end
end end
......
...@@ -10,7 +10,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -10,7 +10,7 @@ class Projects::HooksController < Projects::ApplicationController
@hook = @project.hooks.new(hook_params) @hook = @project.hooks.new(hook_params)
@hook.save @hook.save
unless @hook.valid? unless @hook.valid?
@hooks = @project.hooks.select(&:persisted?) @hooks = @project.hooks.select(&:persisted?)
flash[:alert] = @hook.errors.full_messages.join.html_safe flash[:alert] = @hook.errors.full_messages.join.html_safe
end end
...@@ -49,7 +49,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -49,7 +49,7 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params def hook_params
params.require(:hook).permit( params.require(:hook).permit(
:build_events, :job_events,
:pipeline_events, :pipeline_events,
:enable_ssl_verification, :enable_ssl_verification,
:issues_events, :issues_events,
......
...@@ -16,7 +16,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -16,7 +16,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines] before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check] before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs] before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines] before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines]
before_action :close_merge_request_without_source_project, only: [:show, :diffs, :commits, :builds, :pipelines] before_action :close_merge_request_without_source_project, only: [:show, :diffs, :commits, :builds, :pipelines]
before_action :apply_diff_view_cookie!, only: [:new_diffs] before_action :apply_diff_view_cookie!, only: [:new_diffs]
...@@ -39,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -39,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@collection_type = "MergeRequest" @collection_type = "MergeRequest"
@merge_requests = merge_requests_collection @merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]) @merge_requests = @merge_requests.page(params[:page])
@merge_requests = @merge_requests.includes(merge_request_diff: :merge_request) @merge_requests = @merge_requests.preload(merge_request_diff: :merge_request)
@issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
...@@ -101,34 +100,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -101,34 +100,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html { define_discussion_vars } format.html { define_discussion_vars }
format.json do format.json do
@merge_request_diff = define_diff_vars
if params[:diff_id] define_diff_comment_vars
@merge_request.merge_request_diffs.viewable.find(params[:diff_id])
else
@merge_request.merge_request_diff
end
@merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
@comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
if params[:start_sha].present?
@start_sha = params[:start_sha]
@start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
unless @start_version
@start_sha = @merge_request_diff.head_commit_sha
@start_version = @merge_request_diff
end
end
@environment = @merge_request.environments_for(current_user).last @environment = @merge_request.environments_for(current_user).last
if @start_sha
compared_diff_version
else
original_diff_version
end
render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") }
end end
end end
...@@ -140,16 +116,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -140,16 +116,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diff_for_path def diff_for_path
if params[:id] if params[:id]
merge_request merge_request
define_diff_vars
define_diff_comment_vars define_diff_comment_vars
else else
build_merge_request build_merge_request
@diffs = @merge_request.diffs(diff_options)
@diff_notes_disabled = true @diff_notes_disabled = true
@grouped_diff_discussions = {}
end end
define_commit_vars define_commit_vars
render_diff_for_path(@merge_request.diffs(diff_options)) render_diff_for_path(@diffs)
end end
def commits def commits
...@@ -586,15 +563,46 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -586,15 +563,46 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit @base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
end end
def define_diff_vars
@merge_request_diff =
if params[:diff_id]
@merge_request.merge_request_diffs.viewable.find(params[:diff_id])
else
@merge_request.merge_request_diff
end
@merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
@comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
if params[:start_sha].present?
@start_sha = params[:start_sha]
@start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
unless @start_version
@start_sha = @merge_request_diff.head_commit_sha
@start_version = @merge_request_diff
end
end
@diffs =
if @start_sha
@merge_request_diff.compare_with(@start_sha).diffs(diff_options)
else
@merge_request_diff.diffs(diff_options)
end
end
def define_diff_comment_vars def define_diff_comment_vars
@new_diff_note_attrs = { @new_diff_note_attrs = {
noteable_type: 'MergeRequest', noteable_type: 'MergeRequest',
noteable_id: @merge_request.id noteable_id: @merge_request.id
} }
@diff_notes_disabled = !@merge_request_diff.latest? || @start_sha
@use_legacy_diff_notes = !@merge_request.has_complete_diff_refs? @use_legacy_diff_notes = !@merge_request.has_complete_diff_refs?
@grouped_diff_discussions = @merge_request.grouped_diff_discussions @grouped_diff_discussions = @merge_request.grouped_diff_discussions(@merge_request_diff.diff_refs)
@notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes)) @notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes))
end end
...@@ -678,16 +686,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -678,16 +686,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
end end
def compared_diff_version
@diff_notes_disabled = true
@diffs = @merge_request_diff.compare_with(@start_sha).diffs(diff_options)
end
def original_diff_version
@diff_notes_disabled = !@merge_request_diff.latest?
@diffs = @merge_request_diff.diffs(diff_options)
end
def close_merge_request_without_source_project def close_merge_request_without_source_project
if !@merge_request.source_project && @merge_request.open? if !@merge_request.source_project && @merge_request.open?
@merge_request.close @merge_request.close
......
...@@ -62,6 +62,8 @@ module DiffHelper ...@@ -62,6 +62,8 @@ module DiffHelper
end end
def parallel_diff_discussions(left, right, diff_file) def parallel_diff_discussions(left, right, diff_file)
return unless @grouped_diff_discussions
discussions_left = discussions_right = nil discussions_left = discussions_right = nil
if left && (left.unchanged? || left.removed?) if left && (left.unchanged? || left.removed?)
......
module EventsHelper module EventsHelper
ICON_NAMES_BY_EVENT_TYPE = {
'pushed to' => 'icon_commit',
'pushed new' => 'icon_commit',
'created' => 'icon_status_open',
'opened' => 'icon_status_open',
'closed' => 'icon_status_closed',
'accepted' => 'icon_code_fork',
'commented on' => 'icon_comment_o',
'deleted' => 'icon_trash_o'
}.freeze
def link_to_author(event) def link_to_author(event)
author = event.author author = event.author
...@@ -183,4 +194,21 @@ module EventsHelper ...@@ -183,4 +194,21 @@ module EventsHelper
"event-inline" "event-inline"
end end
end end
def icon_for_event(note)
icon_name = ICON_NAMES_BY_EVENT_TYPE[note]
custom_icon(icon_name) if icon_name
end
def icon_for_profile_event(event)
if current_path?('users#show')
content_tag :div, class: "system-note-image #{event.action_name.parameterize}-icon" do
icon_for_event(event.action_name)
end
else
content_tag :div, class: 'system-note-image user-avatar' do
author_avatar(event, size: 32)
end
end
end
end end
...@@ -3,7 +3,8 @@ module JavascriptHelper ...@@ -3,7 +3,8 @@ module JavascriptHelper
javascript_include_tag asset_path(js) javascript_include_tag asset_path(js)
end end
def page_specific_javascript_bundle_tag(js) # deprecated; use webpack_bundle_tag directly instead
javascript_include_tag(*webpack_asset_paths(js)) def page_specific_javascript_bundle_tag(bundle)
webpack_bundle_tag(bundle)
end end
end end
...@@ -61,12 +61,23 @@ module NotesHelper ...@@ -61,12 +61,23 @@ module NotesHelper
end end
def discussion_diff_path(discussion) def discussion_diff_path(discussion)
return unless discussion.diff_discussion? if discussion.for_merge_request? && discussion.diff_discussion?
if discussion.active?
# Without a diff ID, the link always points to the latest diff version
diff_id = nil
elsif merge_request_diff = discussion.latest_merge_request_diff
diff_id = merge_request_diff.id
else
# If the discussion is not active, and we cannot find the latest
# merge request diff for this discussion, we return no path at all.
return
end
if discussion.for_merge_request? && discussion.active? diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, diff_id: diff_id, anchor: discussion.line_code)
diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code)
elsif discussion.for_commit? elsif discussion.for_commit?
namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code) anchor = discussion.line_code if discussion.diff_discussion?
namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor)
end end
end end
end end
require 'webpack/rails/manifest'
module WebpackHelper
def webpack_bundle_tag(bundle)
javascript_include_tag(*gitlab_webpack_asset_paths(bundle))
end
# override webpack-rails gem helper until changes can make it upstream
def gitlab_webpack_asset_paths(source, extension: nil)
return "" unless source.present?
paths = Webpack::Rails::Manifest.asset_paths(source)
if extension
paths = paths.select { |p| p.ends_with? ".#{extension}" }
end
# include full webpack-dev-server url for rspec tests running locally
if Rails.env.test? && Rails.configuration.webpack.dev_server.enabled
host = Rails.configuration.webpack.dev_server.host
port = Rails.configuration.webpack.dev_server.port
protocol = Rails.configuration.webpack.dev_server.https ? 'https' : 'http'
paths.map! do |p|
"#{protocol}://#{host}:#{port}#{p}"
end
end
paths
end
end
...@@ -326,14 +326,13 @@ class Commit ...@@ -326,14 +326,13 @@ class Commit
end end
def raw_diffs(*args) def raw_diffs(*args)
use_gitaly = Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs) # NOTE: This feature is intentionally disabled until
deltas_only = args.last.is_a?(Hash) && args.last[:deltas_only] # https://gitlab.com/gitlab-org/gitaly/issues/178 is resolved
# if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
if use_gitaly && !deltas_only # Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
Gitlab::GitalyClient::Commit.diff_from_parent(self, *args) # else
else raw.diffs(*args)
raw.diffs(*args) # end
end
end end
def diffs(diff_options = nil) def diffs(diff_options = nil)
......
...@@ -2,11 +2,9 @@ ...@@ -2,11 +2,9 @@
module DiscussionOnDiff module DiscussionOnDiff
extend ActiveSupport::Concern extend ActiveSupport::Concern
included do NUMBER_OF_TRUNCATED_DIFF_LINES = 16
NUMBER_OF_TRUNCATED_DIFF_LINES = 16
memoized_values << :active
included do
delegate :line_code, delegate :line_code,
:original_line_code, :original_line_code,
:diff_file, :diff_file,
...@@ -29,12 +27,6 @@ module DiscussionOnDiff ...@@ -29,12 +27,6 @@ module DiscussionOnDiff
true true
end end
def active?
return @active if @active.present?
@active = first_note.active?
end
# Returns an array of at most 16 highlighted lines above a diff note # Returns an array of at most 16 highlighted lines above a diff note
def truncated_diff_lines(highlight: true) def truncated_diff_lines(highlight: true)
lines = highlight ? highlighted_diff_lines : diff_lines lines = highlight ? highlighted_diff_lines : diff_lines
......
...@@ -25,4 +25,18 @@ module NoteOnDiff ...@@ -25,4 +25,18 @@ module NoteOnDiff
def diff_attributes def diff_attributes
raise NotImplementedError raise NotImplementedError
end end
def active?(diff_refs = nil)
raise NotImplementedError
end
private
def noteable_diff_refs
if noteable.respond_to?(:diff_sha_refs)
noteable.diff_sha_refs
else
noteable.diff_refs
end
end
end end
...@@ -36,10 +36,10 @@ module Noteable ...@@ -36,10 +36,10 @@ module Noteable
.discussions(self) .discussions(self)
end end
def grouped_diff_discussions def grouped_diff_discussions(*args)
# Doesn't use `discussion_notes`, because this may include commit diff notes # Doesn't use `discussion_notes`, because this may include commit diff notes
# besides MR diff notes, that we do no want to display on the MR Changes tab. # besides MR diff notes, that we do no want to display on the MR Changes tab.
notes.inc_relations_for_view.grouped_diff_discussions notes.inc_relations_for_view.grouped_diff_discussions(*args)
end end
def resolvable_discussions def resolvable_discussions
......
...@@ -10,6 +10,7 @@ class DiffDiscussion < Discussion ...@@ -10,6 +10,7 @@ class DiffDiscussion < Discussion
delegate :position, delegate :position,
:original_position, :original_position,
:latest_merge_request_diff,
to: :first_note to: :first_note
......
...@@ -65,20 +65,18 @@ class DiffNote < Note ...@@ -65,20 +65,18 @@ class DiffNote < Note
self.position.diff_refs == diff_refs self.position.diff_refs == diff_refs
end end
def latest_merge_request_diff
return unless for_merge_request?
self.noteable.merge_request_diff_for(self.position.diff_refs)
end
private private
def supported? def supported?
for_commit? || self.noteable.has_complete_diff_refs? for_commit? || self.noteable.has_complete_diff_refs?
end end
def noteable_diff_refs
if noteable.respond_to?(:diff_sha_refs)
noteable.diff_sha_refs
else
noteable.diff_refs
end
end
def set_original_position def set_original_position
self.original_position = self.position.dup unless self.original_position&.complete? self.original_position = self.position.dup unless self.original_position&.complete?
end end
......
...@@ -21,6 +21,8 @@ class Label < ActiveRecord::Base ...@@ -21,6 +21,8 @@ class Label < ActiveRecord::Base
has_many :issues, through: :label_links, source: :target, source_type: 'Issue' has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest' has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest'
before_validation :strip_whitespace_from_title_and_color
validates :color, color: true, allow_blank: false validates :color, color: true, allow_blank: false
# Don't allow ',' for label titles # Don't allow ',' for label titles
...@@ -193,4 +195,8 @@ class Label < ActiveRecord::Base ...@@ -193,4 +195,8 @@ class Label < ActiveRecord::Base
def sanitize_title(value) def sanitize_title(value)
CGI.unescapeHTML(Sanitize.clean(value.to_s)) CGI.unescapeHTML(Sanitize.clean(value.to_s))
end end
def strip_whitespace_from_title_and_color
%w(color title).each { |attr| self[attr] = self[attr]&.strip }
end
end end
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
class LegacyDiffDiscussion < Discussion class LegacyDiffDiscussion < Discussion
include DiscussionOnDiff include DiscussionOnDiff
memoized_values << :active
def legacy_diff_discussion? def legacy_diff_discussion?
true true
end end
...@@ -15,6 +17,12 @@ class LegacyDiffDiscussion < Discussion ...@@ -15,6 +17,12 @@ class LegacyDiffDiscussion < Discussion
LegacyDiffNote LegacyDiffNote
end end
def active?(*args)
return @active if @active.present?
@active = first_note.active?(*args)
end
def collapsed? def collapsed?
!active? !active?
end end
......
...@@ -56,11 +56,12 @@ class LegacyDiffNote < Note ...@@ -56,11 +56,12 @@ class LegacyDiffNote < Note
# #
# If the note's current diff cannot be matched in the MergeRequest's current # If the note's current diff cannot be matched in the MergeRequest's current
# diff, it's considered inactive. # diff, it's considered inactive.
def active? def active?(diff_refs = nil)
return @active if defined?(@active) return @active if defined?(@active)
return true if for_commit? return true if for_commit?
return true unless diff_line return true unless diff_line
return false unless noteable return false unless noteable
return false if diff_refs && diff_refs != noteable_diff_refs
noteable_diff = find_noteable_diff noteable_diff = find_noteable_diff
......
...@@ -366,6 +366,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -366,6 +366,14 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff(true) merge_request_diff(true)
end end
def merge_request_diff_for(diff_refs)
@merge_request_diffs_by_diff_refs ||= Hash.new do |h, diff_refs|
h[diff_refs] = merge_request_diffs.viewable.select_without_diff.find_by_diff_refs(diff_refs)
end
@merge_request_diffs_by_diff_refs[diff_refs]
end
def reload_diff_if_branch_changed def reload_diff_if_branch_changed
if source_branch_changed? || target_branch_changed? if source_branch_changed? || target_branch_changed?
reload_diff reload_diff
......
...@@ -31,6 +31,10 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -31,6 +31,10 @@ class MergeRequestDiff < ActiveRecord::Base
# It allows you to override variables like head_commit_sha before getting diff. # It allows you to override variables like head_commit_sha before getting diff.
after_create :save_git_content, unless: :importing? after_create :save_git_content, unless: :importing?
def self.find_by_diff_refs(diff_refs)
find_by(start_commit_sha: diff_refs.start_sha, head_commit_sha: diff_refs.head_sha, base_commit_sha: diff_refs.base_sha)
end
def self.select_without_diff def self.select_without_diff
select(column_names - ['st_diffs']) select(column_names - ['st_diffs'])
end end
...@@ -130,6 +134,12 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -130,6 +134,12 @@ class MergeRequestDiff < ActiveRecord::Base
st_commits.map { |commit| commit[:id] } st_commits.map { |commit| commit[:id] }
end end
def diff_refs=(new_diff_refs)
self.base_commit_sha = new_diff_refs&.base_sha
self.start_commit_sha = new_diff_refs&.start_sha
self.head_commit_sha = new_diff_refs&.head_sha
end
def diff_refs def diff_refs
return unless start_commit_sha || base_commit_sha return unless start_commit_sha || base_commit_sha
......
...@@ -96,6 +96,7 @@ class Note < ActiveRecord::Base ...@@ -96,6 +96,7 @@ class Note < ActiveRecord::Base
before_validation :set_discussion_id, on: :create before_validation :set_discussion_id, on: :create
after_save :keep_around_commit, unless: :for_personal_snippet? after_save :keep_around_commit, unless: :for_personal_snippet?
after_save :expire_etag_cache after_save :expire_etag_cache
after_destroy :expire_etag_cache
class << self class << self
def model_name def model_name
...@@ -113,11 +114,11 @@ class Note < ActiveRecord::Base ...@@ -113,11 +114,11 @@ class Note < ActiveRecord::Base
Discussion.build(notes) Discussion.build(notes)
end end
def grouped_diff_discussions def grouped_diff_discussions(diff_refs = nil)
diff_notes. diff_notes.
fresh. fresh.
discussions. discussions.
select(&:active?). select { |n| n.active?(diff_refs) }.
group_by(&:line_code) group_by(&:line_code)
end end
...@@ -140,6 +141,10 @@ class Note < ActiveRecord::Base ...@@ -140,6 +141,10 @@ class Note < ActiveRecord::Base
true true
end end
def latest_merge_request_diff
nil
end
def max_attachment_size def max_attachment_size
current_application_settings.max_attachment_size.megabytes.to_i current_application_settings.max_attachment_size.megabytes.to_i
end end
......
...@@ -963,13 +963,15 @@ class Repository ...@@ -963,13 +963,15 @@ class Repository
end end
def is_ancestor?(ancestor_id, descendant_id) def is_ancestor?(ancestor_id, descendant_id)
Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| # NOTE: This feature is intentionally disabled until
if is_enabled # https://gitlab.com/gitlab-org/gitlab-ce/issues/30586 is resolved
raw_repository.is_ancestor?(ancestor_id, descendant_id) # Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
else # if is_enabled
merge_base_commit(ancestor_id, descendant_id) == ancestor_id # raw_repository.is_ancestor?(ancestor_id, descendant_id)
end # else
end merge_base_commit(ancestor_id, descendant_id) == ancestor_id
# end
# end
end end
def empty_repo? def empty_repo?
......
...@@ -28,6 +28,7 @@ class GroupPolicy < BasePolicy ...@@ -28,6 +28,7 @@ class GroupPolicy < BasePolicy
can! :admin_namespace can! :admin_namespace
can! :admin_group_member can! :admin_group_member
can! :change_visibility_level can! :change_visibility_level
can! :create_subgroup if @user.can_create_group
end end
if globally_viewable && @subject.request_access_enabled && !member if globally_viewable && @subject.request_access_enabled && !member
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
= button_to reset_health_check_token_admin_application_settings_path, = button_to reset_health_check_token_admin_application_settings_path,
method: :put, class: 'btn btn-default', method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset the health check token?' } do data: { confirm: 'Are you sure you want to reset the health check token?' } do
= icon('refresh') = icon('spinner')
Reset health check access token Reset health check access token
%p.light %p.light
Health information can be retrieved as plain text, JSON, or XML using: Health information can be retrieved as plain text, JSON, or XML using:
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
= button_to reset_runners_token_admin_application_settings_path, = button_to reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default', method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset registration token?' } do data: { confirm: 'Are you sure you want to reset registration token?' } do
= icon('refresh') = icon('spinner')
Reset runners registration token Reset runners registration token
.bs-callout .bs-callout
......
...@@ -20,21 +20,22 @@ ...@@ -20,21 +20,22 @@
= discussion.author.to_reference = discussion.author.to_reference
started a discussion started a discussion
- url = discussion_diff_path(discussion)
- if discussion.for_commit? && @noteable != discussion.noteable - if discussion.for_commit? && @noteable != discussion.noteable
on on
- commit = discussion.noteable - commit = discussion.noteable
- if commit - if commit
commit commit
- anchor = discussion.line_code if discussion.diff_discussion? = link_to commit.short_id, url, class: 'monospace'
= link_to commit.short_id, namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor), class: 'monospace'
- else - else
a deleted commit a deleted commit
- elsif discussion.diff_discussion? - elsif discussion.diff_discussion?
on on
- if discussion.active? = conditional_link_to url.present?, url do
= link_to 'the diff', discussion_diff_path(discussion) - if discussion.active?
- else the diff
an outdated diff - else
an outdated diff
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago") = time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
= render "discussions/headline", discussion: discussion = render "discussions/headline", discussion: discussion
......
- if event.target = icon_for_profile_event(event)
- if event.action_name == "opened"
.profile-icon.open-icon
= custom_icon("icon_status_open")
- elsif event.action_name == "closed"
.profile-icon.closed-icon
= custom_icon("icon_status_closed")
- else
.profile-icon.fork-icon
= custom_icon("icon_code_fork")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
.profile-icon.open-icon = icon_for_profile_event(event)
= custom_icon("icon_status_open")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
.profile-icon = icon_for_profile_event(event)
= custom_icon("icon_comment_o")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
- project = event.project - project = event.project
.profile-icon = icon_for_profile_event(event)
- if event.action_name == "deleted"
= custom_icon("trash_o")
- else
= custom_icon("icon_commit")
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
.nav-controls .nav-controls
= form_tag request.path, method: :get do |f| = form_tag request.path, method: :get do |f|
= search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name', class: 'form-control', spellcheck: false = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name', class: 'form-control', spellcheck: false
- if can? current_user, :admin_group, @group - if can?(current_user, :create_subgroup, @group)
= link_to new_group_path(parent_id: @group.id), class: 'btn btn-new pull-right' do = link_to new_group_path(parent_id: @group.id), class: 'btn btn-new pull-right' do
New Subgroup New Subgroup
......
...@@ -252,7 +252,7 @@ ...@@ -252,7 +252,7 @@
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title .dropdown-title
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
...@@ -291,7 +291,7 @@ ...@@ -291,7 +291,7 @@
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading .dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading
.dropdown-title .dropdown-title
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
...@@ -335,7 +335,7 @@ ...@@ -335,7 +335,7 @@
= icon('chevron-down') = icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user
.dropdown-title .dropdown-title
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
...@@ -362,7 +362,7 @@ ...@@ -362,7 +362,7 @@
.dropdown-title .dropdown-title
%button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } } %button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } }
= icon('arrow-left') = icon('arrow-left')
%span Dropdown Title %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times') = icon('times')
.dropdown-input .dropdown-input
......
...@@ -28,9 +28,9 @@ ...@@ -28,9 +28,9 @@
= stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print" = stylesheet_link_tag "print", media: "print"
= javascript_include_tag(*webpack_asset_paths("runtime")) = webpack_bundle_tag "runtime"
= javascript_include_tag(*webpack_asset_paths("common")) = webpack_bundle_tag "common"
= javascript_include_tag(*webpack_asset_paths("main")) = webpack_bundle_tag "main"
- if content_for?(:page_specific_javascripts) - if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts = yield :page_specific_javascripts
......
...@@ -25,13 +25,6 @@ ...@@ -25,13 +25,6 @@
#blob-content-holder.blob-content-holder #blob-content-holder.blob-content-holder
%article.file-holder %article.file-holder
= render "projects/blob/header", blob: blob = render "projects/blob/header", blob: blob
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You don't have permission to edit this file. Try forking this project to edit the file.
= link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
Cancel
- if blob.empty? - if blob.empty?
.file-content.code .file-content.code
......
...@@ -38,3 +38,10 @@ ...@@ -38,3 +38,10 @@
- if current_user - if current_user
= replace_blob_link = replace_blob_link
= delete_blob_link = delete_blob_link
- if current_user
.js-file-fork-suggestion-section.file-fork-suggestion.hidden
%span.file-fork-suggestion-note
You don't have permission to edit this file. Try forking this project to edit the file.
= link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
%button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
Cancel
...@@ -136,7 +136,7 @@ ...@@ -136,7 +136,7 @@
- else - else
= build.id = build.id
- if build.retried? - if build.retried?
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' } %i.fa.fa-spinner.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
:javascript :javascript
new Sidebar(); new Sidebar();
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
= icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.') = icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.')
- if retried - if retried
= icon('refresh', class: 'text-warning has-tooltip', title: 'Job was retried') = icon('spinner', class: 'text-warning has-tooltip', title: 'Job was retried')
.label-container .label-container
- if job.tags.any? - if job.tags.any?
......
...@@ -5,8 +5,7 @@ ...@@ -5,8 +5,7 @@
- left = line[:left] - left = line[:left]
- right = line[:right] - right = line[:right]
- last_line = right.new_pos if right - last_line = right.new_pos if right
- unless @diff_notes_disabled - discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file)
- discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file)
%tr.line_holder.parallel %tr.line_holder.parallel
- if left - if left
- case left.type - case left.type
......
...@@ -4,11 +4,10 @@ ...@@ -4,11 +4,10 @@
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show. %a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
%table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' } %table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' }
- discussions = @grouped_diff_discussions unless @diff_notes_disabled
= render partial: "projects/diffs/line", = render partial: "projects/diffs/line",
collection: diff_file.highlighted_diff_lines, collection: diff_file.highlighted_diff_lines,
as: :line, as: :line,
locals: { diff_file: diff_file, discussions: discussions } locals: { diff_file: diff_file, discussions: @grouped_diff_discussions }
- if !diff_file.new_file && !diff_file.deleted_file && diff_file.highlighted_diff_lines.any? - if !diff_file.new_file && !diff_file.deleted_file && diff_file.highlighted_diff_lines.any?
- last_line = diff_file.highlighted_diff_lines.last - last_line = diff_file.highlighted_diff_lines.last
......
...@@ -72,13 +72,16 @@ ...@@ -72,13 +72,16 @@
= link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do = link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do
new commits new commits
from from
%code= @merge_request.target_branch = succeed '.' do
%code= @merge_request.target_branch
- unless @merge_request_diff.latest? && !@start_sha - if @diff_notes_disabled
.comments-disabled-notif.content-block .comments-disabled-notif.content-block
= icon('info-circle') = icon('info-circle')
- if @start_sha - if @start_sha
Comments are disabled because you're comparing two versions of this merge request. Comments are disabled because you're comparing two versions of this merge request.
- else - else
Comments are disabled because you're viewing an old version of this merge request. Discussions on this version of the merge request are displayed but comment creation is disabled.
= link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
.pull-right
= link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M9.427 6.523a.932.932 0 0 0-.808.489v-.01c-.49-.01-1.059-.172-1.46-.489-.35-.278-.7-.772-.882-1.17a.964.964 0 0 0 .35-.744.943.943 0 0 0-.934-.959c-.518 0-.933.432-.933.964 0 .35.191.662.467.825v3.147a.97.97 0 0 0-.467.825c0 .532.415.959.933.959a.943.943 0 0 0 .934-.96.965.965 0 0 0-.467-.824V6.844c.313.336.672.61 1.073.81.402.202.948.303 1.386.308v-.01c.168.293.467.49.808.49a.943.943 0 0 0 .933-.96.943.943 0 0 0-.933-.96z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m2 3c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m.761.85c.154 2.556 1.987 4.692 4.45 5.255.328-.655 1.01-1.105 1.789-1.105 1.105 0 2 .895 2 2 0 1.105-.895 2-2 2-.89 0-1.645-.582-1.904-1.386-1.916-.376-3.548-1.5-4.596-3.044v4.493c.863.222 1.5 1.01 1.5 1.937 0 1.105-.895 2-2 2-1.105 0-2-.895-2-2 0-.74.402-1.387 1-1.732v-8.535c-.598-.346-1-.992-1-1.732 0-1.105.895-2 2-2 1.105 0 2 .895 2 2 0 .835-.512 1.551-1.239 1.85m6.239 7.15c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m-7 4c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1" transform="translate(3)"/></svg>
...@@ -3,7 +3,6 @@ class BuildCoverageWorker ...@@ -3,7 +3,6 @@ class BuildCoverageWorker
include BuildQueue include BuildQueue
def perform(build_id) def perform(build_id)
Ci::Build.find_by(id: build_id) Ci::Build.find_by(id: build_id)&.update_coverage
.try(:update_coverage)
end end
end end
---
title: Fix filtered search input width for IE
merge_request:
author:
---
title: Update all instances of the old loading icon
merge_request: 10490
author: Andrew Torres
---
title: Add webpack_bundle_tag helper to improve non-localhost GDK configurations
merge_request: 10604
author:
---
title: Separate CE params on Grape API
merge_request:
author:
---
title: Turns true value and false value database methods from instance to class methods
merge_request: 10583
author:
---
title: Fix issue's note cache expiration after delete
merge_request:
author: mhasbini
---
title: "[BB Importer] Save the error trace and the whole raw document to debug problems
easier"
merge_request:
author:
---
title: Link to outdated diff in older MR version from outdated diff discussion
merge_request:
author:
---
title: Fix missing capitalisation on views
merge_request:
author:
---
title: Fix bad query for PostgreSQL showing merge requests list
merge_request: 10666
author:
---
title: Remove heading and trailing spaces from label's color and title
merge_request: 10603
author: blackst0ne
---
title: "Make the `gitlab:gitlab_shell:check` task check that the repositories storage path are owned by the `root` group"
merge_request:
author:
---
title: Reset New branch button when issue state changes
merge_request: 5962
author: winniehell
---
title: Hide new subgroup button if user has no permission to create one
merge_request: 10627
author:
---
title: Fix preemptive scroll bar on user activity calendar.
merge_request: !10636
author:
---
title: "Bugfix: POST /projects/:id/hooks and PUT /projects/:id/hook/:hook_id no longer ignore the the job_events param in the V4 API"
merge_request: 10586
author:
...@@ -579,9 +579,9 @@ test: ...@@ -579,9 +579,9 @@ test:
storages: storages:
default: default:
path: tmp/tests/repositories/ path: tmp/tests/repositories/
gitaly_address: unix:<%= Rails.root.join('tmp/sockets/private/gitaly.socket') %> gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
gitaly: gitaly:
enabled: false enabled: true
backup: backup:
path: tmp/tests/backups path: tmp/tests/backups
gitlab_shell: gitlab_shell:
......
...@@ -11,6 +11,7 @@ var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeMod ...@@ -11,6 +11,7 @@ var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeMod
var ROOT_PATH = path.resolve(__dirname, '..'); var ROOT_PATH = path.resolve(__dirname, '..');
var IS_PRODUCTION = process.env.NODE_ENV === 'production'; var IS_PRODUCTION = process.env.NODE_ENV === 'production';
var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1; var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1;
var DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808; var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false'; var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
var WEBPACK_REPORT = process.env.WEBPACK_REPORT; var WEBPACK_REPORT = process.env.WEBPACK_REPORT;
...@@ -182,12 +183,13 @@ if (IS_PRODUCTION) { ...@@ -182,12 +183,13 @@ if (IS_PRODUCTION) {
if (IS_DEV_SERVER) { if (IS_DEV_SERVER) {
config.devtool = 'cheap-module-eval-source-map'; config.devtool = 'cheap-module-eval-source-map';
config.devServer = { config.devServer = {
host: DEV_SERVER_HOST,
port: DEV_SERVER_PORT, port: DEV_SERVER_PORT,
headers: { 'Access-Control-Allow-Origin': '*' }, headers: { 'Access-Control-Allow-Origin': '*' },
stats: 'errors-only', stats: 'errors-only',
inline: DEV_SERVER_LIVERELOAD inline: DEV_SERVER_LIVERELOAD
}; };
config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath; config.output.publicPath = '//' + DEV_SERVER_HOST + ':' + DEV_SERVER_PORT + config.output.publicPath;
config.plugins.push( config.plugins.push(
// watch node_modules for changes if we encounter a missing module compile error // watch node_modules for changes if we encounter a missing module compile error
new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules')) new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
......
# rubocop:disable all # rubocop:disable all
class ConvertClosedToStateInIssue < ActiveRecord::Migration class ConvertClosedToStateInIssue < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}" execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}"
......
# rubocop:disable all # rubocop:disable all
class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
execute "UPDATE #{table_name} SET state = 'merged' WHERE closed = #{true_value} AND merged = #{true_value}" execute "UPDATE #{table_name} SET state = 'merged' WHERE closed = #{true_value} AND merged = #{true_value}"
......
# rubocop:disable all # rubocop:disable all
class ConvertClosedToStateInMilestone < ActiveRecord::Migration class ConvertClosedToStateInMilestone < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}" execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}"
......
# rubocop:disable all # rubocop:disable all
class UserColorScheme < ActiveRecord::Migration class UserColorScheme < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
add_column :users, :color_scheme_id, :integer, null: false, default: 1 add_column :users, :color_scheme_id, :integer, null: false, default: 1
......
# rubocop:disable all # rubocop:disable all
class AddVisibilityLevelToProjects < ActiveRecord::Migration class AddVisibilityLevelToProjects < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def self.up def self.up
add_column :projects, :visibility_level, :integer, :default => 0, :null => false add_column :projects, :visibility_level, :integer, :default => 0, :null => false
......
# rubocop:disable all # rubocop:disable all
class MigrateAlreadyImportedProjects < ActiveRecord::Migration class MigrateAlreadyImportedProjects < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
execute("UPDATE projects SET import_status = 'finished' WHERE imported = #{true_value}") execute("UPDATE projects SET import_status = 'finished' WHERE imported = #{true_value}")
......
# rubocop:disable all # rubocop:disable all
class AddVisibilityLevelToSnippet < ActiveRecord::Migration class AddVisibilityLevelToSnippet < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
add_column :snippets, :visibility_level, :integer, :default => 0, :null => false add_column :snippets, :visibility_level, :integer, :default => 0, :null => false
......
# rubocop:disable all # rubocop:disable all
class MigrateCiWebHooks < ActiveRecord::Migration class MigrateCiWebHooks < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
execute( execute(
......
# rubocop:disable all # rubocop:disable all
class MigrateCiEmails < ActiveRecord::Migration class MigrateCiEmails < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
# This inserts a new service: BuildsEmailService # This inserts a new service: BuildsEmailService
......
# rubocop:disable all # rubocop:disable all
class MigrateCiSlackService < ActiveRecord::Migration class MigrateCiSlackService < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
properties_query = 'SELECT properties FROM ci_services ' \ properties_query = 'SELECT properties FROM ci_services ' \
......
# rubocop:disable all # rubocop:disable all
class MigrateCiHipChatService < ActiveRecord::Migration class MigrateCiHipChatService < ActiveRecord::Migration
include Gitlab::Database include Gitlab::Database::MigrationHelpers
def up def up
# From properties strip `hipchat_` key # From properties strip `hipchat_` key
......
class MigrateBuildEventsToPipelineEvents < ActiveRecord::Migration class MigrateBuildEventsToPipelineEvents < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
include Gitlab::Database
DOWNTIME = false DOWNTIME = false
......
doc/ci/img/pipelines.png

7.34 KB | W: | H:

doc/ci/img/pipelines.png

6.15 KB | W: | H:

doc/ci/img/pipelines.png
doc/ci/img/pipelines.png
doc/ci/img/pipelines.png
doc/ci/img/pipelines.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -48,8 +48,8 @@ Steps to split page-specific JavaScript from the main `main.js`: ...@@ -48,8 +48,8 @@ Steps to split page-specific JavaScript from the main `main.js`:
```haml ```haml
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('lib_chart') = webpack_bundle_tag 'lib_chart'
= page_specific_javascript_bundle_tag('graphs') = webpack_bundle_tag 'graphs'
``` ```
The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js` The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js`
......
...@@ -13,10 +13,19 @@ for more information on general testing practices at GitLab. ...@@ -13,10 +13,19 @@ for more information on general testing practices at GitLab.
## Karma test suite ## Karma test suite
GitLab uses the [Karma][karma] test runner with [Jasmine][jasmine] as its test GitLab uses the [Karma][karma] test runner with [Jasmine][jasmine] as its test
framework for our JavaScript unit tests. For tests that rely on DOM framework for our JavaScript unit tests. For tests that rely on DOM
manipulation we use fixtures which are pre-compiled from HAML source files and manipulation we use fixtures which are pre-compiled from HAML source files and
served during testing by the [jasmine-jquery][jasmine-jquery] plugin. served during testing by the [jasmine-jquery][jasmine-jquery] plugin.
JavaScript tests live in `spec/javascripts/`, matching the folder structure
of `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js`
has a corresponding `spec/javascripts/behaviors/autosize_spec.js` file.
Keep in mind that in a CI environment, these tests are run in a headless
browser and you will not have access to certain APIs, such as
[`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification),
which will have to be stubbed.
### Running frontend tests ### Running frontend tests
`rake karma` runs the frontend-only (JavaScript) tests. `rake karma` runs the frontend-only (JavaScript) tests.
...@@ -80,24 +89,23 @@ If an integration test depends on JavaScript to run correctly, you need to make ...@@ -80,24 +89,23 @@ If an integration test depends on JavaScript to run correctly, you need to make
sure the spec is configured to enable JavaScript when the tests are run. If you sure the spec is configured to enable JavaScript when the tests are run. If you
don't do this you'll see vague error messages from the spec runner. don't do this you'll see vague error messages from the spec runner.
To enable a JavaScript driver in an `rspec` test, add `js: true` to the To enable a JavaScript driver in an `rspec` test, add `:js` to the
individual spec or the context block containing multiple specs that need individual spec or the context block containing multiple specs that need
JavaScript enabled: JavaScript enabled:
```ruby ```ruby
# For one spec # For one spec
it 'presents information about abuse report', js: true do it 'presents information about abuse report', :js do
# assertions... # assertions...
end end
describe "Admin::AbuseReports", js: true do describe "Admin::AbuseReports", :js do
it 'presents information about abuse report' do it 'presents information about abuse report' do
# assertions... # assertions...
end end
it 'shows buttons for adding to abuse report' do it 'shows buttons for adding to abuse report' do
# assertions... # assertions...
end end
end end
``` ```
...@@ -113,13 +121,12 @@ file for the failing spec, add the `@javascript` flag above the Scenario: ...@@ -113,13 +121,12 @@ file for the failing spec, add the `@javascript` flag above the Scenario:
``` ```
@javascript @javascript
Scenario: Developer can approve merge request Scenario: Developer can approve merge request
Given I am a "Shop" developer Given I am a "Shop" developer
And I visit project "Shop" merge requests page And I visit project "Shop" merge requests page
And merge request 'Bug NS-04' must be approved And merge request 'Bug NS-04' must be approved
And I click link "Bug NS-04" And I click link "Bug NS-04"
When I click link "Approve" When I click link "Approve"
Then I should see approved merge request "Bug NS-04" Then I should see approved merge request "Bug NS-04"
``` ```
[capybara]: http://teamcapybara.github.io/capybara/ [capybara]: http://teamcapybara.github.io/capybara/
......
...@@ -4,28 +4,53 @@ When writing migrations for GitLab, you have to take into account that ...@@ -4,28 +4,53 @@ When writing migrations for GitLab, you have to take into account that
these will be ran by hundreds of thousands of organizations of all sizes, some with these will be ran by hundreds of thousands of organizations of all sizes, some with
many years of data in their database. many years of data in their database.
In addition, having to take a server offline for a an upgrade small or big is In addition, having to take a server offline for a a upgrade small or big is a
a big burden for most organizations. For this reason it is important that your big burden for most organizations. For this reason it is important that your
migrations are written carefully, can be applied online and adhere to the style guide below. migrations are written carefully, can be applied online and adhere to the style
guide below.
Migrations should not require GitLab installations to be taken offline unless Migrations are **not** allowed to require GitLab installations to be taken
_absolutely_ necessary - see the ["What Requires Downtime?"](what_requires_downtime.md) offline unless _absolutely necessary_. Downtime assumptions should be based on
page. If a migration requires downtime, this should be clearly mentioned during the behaviour of a migration when performed using PostgreSQL, as various
the review process, as well as being documented in the monthly release post. For operations in MySQL may require downtime without there being alternatives.
more information, see the "Downtime Tagging" section below.
When downtime is necessary the migration has to be approved by:
1. The VP of Engineering
1. A Backend Lead
1. A Database Specialist
An up-to-date list of people holding these titles can be found at
<https://about.gitlab.com/team/>.
The document ["What Requires Downtime?"](what_requires_downtime.md) specifies
various database operations, whether they require downtime and how to
work around that whenever possible.
When writing your migrations, also consider that databases might have stale data When writing your migrations, also consider that databases might have stale data
or inconsistencies and guard for that. Try to make as little assumptions as possible or inconsistencies and guard for that. Try to make as few assumptions as
about the state of the database. possible about the state of the database.
Please don't depend on GitLab-specific code since it can change in future
versions. If needed copy-paste GitLab code into the migration to make it forward
compatible.
## Commit Guidelines
Please don't depend on GitLab specific code since it can change in future versions. Each migration **must** be added in its own commit with a descriptive commit
If needed copy-paste GitLab code into the migration to make it forward compatible. message. If a commit adds a migration it _should only_ include the migration and
any corresponding changes to `db/schema.rb`. This makes it easy to revert a
database migration without accidentally reverting other changes.
## Downtime Tagging ## Downtime Tagging
Every migration must specify if it requires downtime or not, and if it should Every migration must specify if it requires downtime or not, and if it should
require downtime it must also specify a reason for this. To do so, add the require downtime it must also specify a reason for this. This is required even
following two constants to the migration class' body: if 99% of the migrations won't require downtime as this makes it easier to find
the migrations that _do_ require downtime.
To tag a migration, add the following two constants to the migration class'
body:
* `DOWNTIME`: a boolean that when set to `true` indicates the migration requires * `DOWNTIME`: a boolean that when set to `true` indicates the migration requires
downtime. downtime.
...@@ -50,12 +75,53 @@ from a migration class. ...@@ -50,12 +75,53 @@ from a migration class.
## Reversibility ## Reversibility
Your migration should be reversible. This is very important, as it should Your migration **must be** reversible. This is very important, as it should
be possible to downgrade in case of a vulnerability or bugs. be possible to downgrade in case of a vulnerability or bugs.
In your migration, add a comment describing how the reversibility of the In your migration, add a comment describing how the reversibility of the
migration was tested. migration was tested.
## Multi Threading
Sometimes a migration might need to use multiple Ruby threads to speed up a
migration. For this to work your migration needs to include the module
`Gitlab::Database::MultiThreadedMigration`:
```ruby
class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::Database::MultiThreadedMigration
end
```
You can then use the method `with_multiple_threads` to perform work in separate
threads. For example:
```ruby
class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::Database::MultiThreadedMigration
def up
with_multiple_threads(4) do
disable_statement_timeout
# ...
end
end
end
```
Here the call to `disable_statement_timeout` will use the connection local to
the `with_multiple_threads` block, instead of re-using the global connection
pool. This ensures each thread has its own connection object, and won't time
out when trying to obtain one.
**NOTE:** PostgreSQL has a maximum amount of connections that it allows. This
limit can vary from installation to installation. As a result it's recommended
you do not use more than 32 threads in a single migration. Usually 4-8 threads
should be more than enough.
## Removing indices ## Removing indices
When removing an index make sure to use the method `remove_concurrent_index` instead When removing an index make sure to use the method `remove_concurrent_index` instead
...@@ -78,7 +144,10 @@ end ...@@ -78,7 +144,10 @@ end
## Adding indices ## Adding indices
If you need to add an unique index please keep in mind there is possibility of existing duplicates. If it is possible write a separate migration for handling this situation. It can be just removing or removing with overwriting all references to these duplicates depend on situation. If you need to add a unique index please keep in mind there is the possibility
of existing duplicates being present in the database. This means that should
always _first_ add a migration that removes any duplicates, before adding the
unique index.
When adding an index make sure to use the method `add_concurrent_index` instead When adding an index make sure to use the method `add_concurrent_index` instead
of the regular `add_index` method. The `add_concurrent_index` method of the regular `add_index` method. The `add_concurrent_index` method
...@@ -90,17 +159,22 @@ so: ...@@ -90,17 +159,22 @@ so:
```ruby ```ruby
class MyMigration < ActiveRecord::Migration class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
disable_ddl_transaction! disable_ddl_transaction!
def change def up
add_concurrent_index :table, :column
end
def down
remove_index :table, :column if index_exists?(:table, :column)
end end
end end
``` ```
## Adding Columns With Default Values ## Adding Columns With Default Values
When adding columns with default values you should use the method When adding columns with default values you must use the method
`add_column_with_default`. This method ensures the table is updated without `add_column_with_default`. This method ensures the table is updated without
requiring downtime. This method is not reversible so you must manually define requiring downtime. This method is not reversible so you must manually define
the `up` and `down` methods in your migration class. the `up` and `down` methods in your migration class.
...@@ -123,6 +197,9 @@ class MyMigration < ActiveRecord::Migration ...@@ -123,6 +197,9 @@ class MyMigration < ActiveRecord::Migration
end end
``` ```
Keep in mind that this operation can easily take 10-15 minutes to complete on
larger installations (e.g. GitLab.com). As a result you should only add default
values if absolutely necessary.
## Integer column type ## Integer column type
...@@ -147,13 +224,15 @@ add_column(:projects, :foo, :integer, default: 10, limit: 8) ...@@ -147,13 +224,15 @@ add_column(:projects, :foo, :integer, default: 10, limit: 8)
## Testing ## Testing
Make sure that your migration works with MySQL and PostgreSQL with data. An empty database does not guarantee that your migration is correct. Make sure that your migration works with MySQL and PostgreSQL with data. An
empty database does not guarantee that your migration is correct.
Make sure your migration can be reversed. Make sure your migration can be reversed.
## Data migration ## Data migration
Please prefer Arel and plain SQL over usual ActiveRecord syntax. In case of using plain SQL you need to quote all input manually with `quote_string` helper. Please prefer Arel and plain SQL over usual ActiveRecord syntax. In case of
using plain SQL you need to quote all input manually with `quote_string` helper.
Example with Arel: Example with Arel:
...@@ -177,3 +256,17 @@ select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(i ...@@ -177,3 +256,17 @@ select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(i
execute("DELETE FROM tags WHERE id IN(#{duplicate_ids.join(",")})") execute("DELETE FROM tags WHERE id IN(#{duplicate_ids.join(",")})")
end end
``` ```
If you need more complex logic you can define and use models local to a
migration. For example:
```ruby
class MyMigration < ActiveRecord::Migration
class Project < ActiveRecord::Base
self.table_name = 'projects'
end
end
```
When doing so be sure to explicitly set the model's table name so it's not
derived from the class name or namespace.
This diff is collapsed.
This diff is collapsed.
...@@ -289,9 +289,9 @@ sudo usermod -aG redis git ...@@ -289,9 +289,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 9-0-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-1-stable gitlab
**Note:** You can change `9-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! **Note:** You can change `9-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It ### Configure It
...@@ -475,7 +475,7 @@ with setting up Gitaly until you upgrade to GitLab 9.2 or later. ...@@ -475,7 +475,7 @@ with setting up Gitaly until you upgrade to GitLab 9.2 or later.
sudo -u git cp config.toml.example config.toml sudo -u git cp config.toml.example config.toml
# If you are using non-default settings you need to update config.toml # If you are using non-default settings you need to update config.toml
sudo -u git -H editor config.toml sudo -u git -H editor config.toml
# Enable Gitaly in the init script # Enable Gitaly in the init script
echo 'gitaly_enabled=true' | sudo tee -a /etc/default/gitlab echo 'gitaly_enabled=true' | sudo tee -a /etc/default/gitlab
......
# Authentication
This page gathers all the resources for the topic **Authentication** within GitLab.
## GitLab users
- [SSH](../../ssh/README.md)
- [Two-Factor Authentication (2FA)](../../user/profile/account/two_factor_authentication.md#two-factor-authentication)
- **Articles:**
- [Support for Universal 2nd Factor Authentication - YubiKeys](https://about.gitlab.com/2016/06/22/gitlab-adds-support-for-u2f/)
- [Security Webcast with Yubico](https://about.gitlab.com/2016/08/31/gitlab-and-yubico-security-webcast/)
- **Integrations:**
- [GitLab as OAuth2 authentication service provider](../../integration/oauth_provider.md#introduction-to-oauth)
## GitLab administrators
- [LDAP (Community Edition)](../../administration/auth/ldap.md)
- [LDAP (Enterprise Edition)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html)
- [Enforce Two-factor Authentication (2FA)](../../security/two_factor_authentication.md#enforce-two-factor-authentication-2fa)
- **Articles:**
- [Feature Highlight: LDAP Integration](https://about.gitlab.com/2014/07/10/feature-highlight-ldap-sync/)
- [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/ldap/debugging_ldap.html)
- **Integrations:**
- [OmniAuth](../../integration/omniauth.md)
- [Authentiq OmniAuth Provider](../../administration/auth/authentiq.md#authentiq-omniauth-provider)
- [Atlassian Crowd OmniAuth Provider](../../administration/auth/crowd.md)
- [CAS OmniAuth Provider](../../integration/cas.md)
- [SAML OmniAuth Provider](../../integration/saml.md)
- [Okta SSO provider](../../administration/auth/okta.md)
- [Kerberos integration (GitLab EE)](https://docs.gitlab.com/ee/integration/kerberos.html)
## API
- [OAuth 2 Tokens](../../api/README.md#oauth-2-tokens)
- [Private Tokens](../../api/README.md#private-tokens)
- [Impersonation tokens](../../api/README.md#impersonation-tokens)
- [GitLab as an OAuth2 provider](../../api/oauth2.md#gitlab-as-an-oauth2-provider)
- [GitLab Runner API - Authentication](../../api/ci/runners.md#authentication)
## Third-party resources
- [Kanboard Plugin GitLab Authentication](https://kanboard.net/plugin/gitlab-auth)
- [Jenkins GitLab OAuth Plugin](https://wiki.jenkins-ci.org/display/JENKINS/GitLab+OAuth+Plugin)
- [Setup Gitlab CE with Active Directory authentication](https://www.caseylabs.com/setup-gitlab-ce-with-active-directory-authentication/)
- [How to customize GitLab to support OpenID authentication](http://eric.van-der-vlist.com/blog/2013/11/23/how-to-customize-gitlab-to-support-openid-authentication/)
- [Openshift - Configuring Authentication and User Agent](https://docs.openshift.org/latest/install_config/configuring_authentication.html#GitLab)
# Git documentation
Git is a [free and open source](https://git-scm.com/about/free-and-open-source)
distributed version control system designed to handle everything from small to
very large projects with speed and efficiency.
[GitLab](https://about.gitlab.com) is a Git-based fully integrated platform for
software development. Besides Git's functionalities, GitLab has a lot of
powerful [features](https://about.gitlab.com/features/) to enhance your
[workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
We've gathered some resources to help you to get the best from Git with GitLab.
## Getting started
- [Git concepts](../../university/training/user_training.md#git-concepts)
- [Start using Git on the command line](../../gitlab-basics/start-using-git.md)
- [Command Line basic commands](../../gitlab-basics/command-line-commands.md)
- [GitLab Git Cheat Sheet (download)](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf)
- **Articles:**
- [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/)
- [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/)
- **Presentations:**
- [GLU Course: About Version Control](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit?usp=sharing)
- **Third-party resources:**
- What is [Git](https://git-scm.com)
- [Version control](https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control)
- [Getting Started - Git Basics](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)
- [Getting Started - Installing Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [Git on the Server - GitLab](https://git-scm.com/book/en/v2/Git-on-the-Server-GitLab)
## Branching strategies
- **Articles:**
- [GitLab Flow](https://about.gitlab.com/2014/09/29/gitlab-flow/)
- **Third-party resources:**
- [Git Branching - Branches in a Nutshell](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell)
- [Git Branching - Branching Workflows](https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows)
## Advanced use
- [Custom Git Hooks](../../administration/custom_hooks.md)
- [Git Attributes](../../user/project/git_attributes.md)
- Git Submodules: [Using Git submodules with GitLab CI](../../ci/git_submodules.md#using-git-submodules-with-gitlab-ci)
## API
- [Gitignore templates](../../api/templates/gitignores.md)
## Git LFS
- [Git LFS](../../workflow/lfs/manage_large_binaries_with_git_lfs.md)
- [Git-Annex to Git-LFS migration guide](https://docs.gitlab.com/ee/workflow/lfs/migrate_from_git_annex_to_git_lfs.html)
- **Articles:**
- [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/)
- [Towards a production quality open source Git LFS server](https://about.gitlab.com/2015/08/13/towards-a-production-quality-open-source-git-lfs-server/)
## General information
- **Articles:**
- [The future of SaaS hosted Git repository pricing](https://about.gitlab.com/2016/05/11/git-repository-pricing/)
...@@ -7,10 +7,10 @@ you through better understanding GitLab's concepts ...@@ -7,10 +7,10 @@ you through better understanding GitLab's concepts
through our regular docs, and, when available, through articles (guides, through our regular docs, and, when available, through articles (guides,
tutorials, technical overviews, blog posts) and videos. tutorials, technical overviews, blog posts) and videos.
- [GitLab Installation](../install/README.md) - [Authentication](authentication/index.md)
- [Continuous Integration (GitLab CI)](../ci/README.md) - [Continuous Integration (GitLab CI)](../ci/README.md)
- [Git](git/index.md)
- [GitLab Installation](../install/README.md)
- [GitLab Pages](../user/project/pages/index.md) - [GitLab Pages](../user/project/pages/index.md)
>**Note:** >**Note:** More topics will be available soon.
Non-linked topics are currently under development and subjected to change.
More topics will be available soon.
# From 9.0 to 9.1 # From 9.0 to 9.1
** TODO: **
# TODO clean out 9.0-specific stuff
Make sure you view this update guide from the tag (version) of GitLab you would Make sure you view this update guide from the tag (version) of GitLab you would
like to install. In most cases this should be the highest numbered production like to install. In most cases this should be the highest numbered production
tag (without rc in it). You can select the tag in the version dropdown at the tag (without rc in it). You can select the tag in the version dropdown at the
......
...@@ -48,6 +48,23 @@ GitLab provides official Docker images for both Community and Enterprise ...@@ -48,6 +48,23 @@ GitLab provides official Docker images for both Community and Enterprise
editions. They are based on the Omnibus package and instructions on how to editions. They are based on the Omnibus package and instructions on how to
update them are in [a separate document][omnidocker]. update them are in [a separate document][omnidocker].
## Upgrading without downtime
Starting with GitLab 9.1.0 it's possible to upgrade to a newer version of GitLab
without having to take your GitLab instance offline. However, for this to work
there are the following requirements:
1. You can only upgrade 1 release at a time. For example, if 9.1.15 is the last
release of 9.1 then you can safely upgrade from that version to 9.2.0.
However, if you are running 9.1.14 you first need to upgrade to 9.1.15.
2. You have to use [post-deployment
migrations](../development/post_deployment_migrations.md).
3. You are using PostgreSQL. If you are using MySQL you will still need downtime
when upgrading.
This applies to major, minor, and patch releases unless stated otherwise in a
release post.
## Upgrading between editions ## Upgrading between editions
GitLab comes in two flavors: [Community Edition][ce] which is MIT licensed, GitLab comes in two flavors: [Community Edition][ce] which is MIT licensed,
......
...@@ -5,10 +5,10 @@ ...@@ -5,10 +5,10 @@
Cycle Analytics measures the time it takes to go from an [idea to production] for Cycle Analytics measures the time it takes to go from an [idea to production] for
each project you have. This is achieved by not only indicating the total time it each project you have. This is achieved by not only indicating the total time it
takes to reach at that point, but the total time is broken down into the takes to reach that point, but the total time is broken down into the
multiple stages an idea has to pass through to be shipped. multiple stages an idea has to pass through to be shipped.
Cycle Analytics is that it is tightly coupled with the [GitLab flow] and Cycle Analytics is tightly coupled with the [GitLab flow] and
calculates a separate median for each stage. calculates a separate median for each stage.
## Overview ## Overview
......
...@@ -60,7 +60,7 @@ to use terminals. Support is currently limited to the first container in the ...@@ -60,7 +60,7 @@ to use terminals. Support is currently limited to the first container in the
first pod of your environment. first pod of your environment.
When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals) When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals)
support to your environments. This is based on the `exec` functionality found in support to your [environments](../../../ci/environments.md). This is based on the `exec` functionality found in
Docker and Kubernetes, so you get a new shell session within your existing Docker and Kubernetes, so you get a new shell session within your existing
containers. To use this integration, you should deploy to Kubernetes using containers. To use this integration, you should deploy to Kubernetes using
the deployment variables above, ensuring any pods you create are labelled with the deployment variables above, ensuring any pods you create are labelled with
......
...@@ -5,11 +5,16 @@ module API ...@@ -5,11 +5,16 @@ module API
before { authenticate! } before { authenticate! }
helpers do helpers do
params :optional_params do params :optional_params_ce do
optional :description, type: String, desc: 'The description of the group' optional :description, type: String, desc: 'The description of the group'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group' optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group' optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access' optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
end
params :optional_params do
use :optional_params_ce
end end
params :statistics_params do params :statistics_params do
......
...@@ -30,7 +30,7 @@ module API ...@@ -30,7 +30,7 @@ module API
use :pagination use :pagination
end end
params :issue_params do params :issue_params_ce do
optional :description, type: String, desc: 'The description of an issue' optional :description, type: String, desc: 'The description of an issue'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue' optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue' optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
...@@ -38,6 +38,10 @@ module API ...@@ -38,6 +38,10 @@ module API
optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY' optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential' optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
end end
params :issue_params do
use :issue_params_ce
end
end end
resource :issues do resource :issues do
......
...@@ -33,13 +33,17 @@ module API ...@@ -33,13 +33,17 @@ module API
end end
end end
params :optional_params do params :optional_params_ce do
optional :description, type: String, desc: 'The description of the merge request' optional :description, type: String, desc: 'The description of the merge request'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request' optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request' optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
optional :labels, type: String, desc: 'Comma-separated list of label names' optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging' optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
end end
params :optional_params do
use :optional_params_ce
end
end end
desc 'List merge requests' do desc 'List merge requests' do
...@@ -145,14 +149,24 @@ module API ...@@ -145,14 +149,24 @@ module API
success Entities::MergeRequest success Entities::MergeRequest
end end
params do params do
# CE
at_least_one_of_ce = [
:assignee_id,
:description,
:labels,
:milestone_id,
:remove_source_branch,
:state_event,
:target_branch,
:title
]
optional :title, type: String, allow_blank: false, desc: 'The title of the merge request' optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
optional :target_branch, type: String, allow_blank: false, desc: 'The target branch' optional :target_branch, type: String, allow_blank: false, desc: 'The target branch'
optional :state_event, type: String, values: %w[close reopen], optional :state_event, type: String, values: %w[close reopen],
desc: 'Status of the merge request' desc: 'Status of the merge request'
use :optional_params use :optional_params
at_least_one_of :title, :target_branch, :description, :assignee_id, at_least_one_of(*at_least_one_of_ce)
:milestone_id, :labels, :state_event,
:remove_source_branch
end end
put ':id/merge_requests/:merge_request_iid' do put ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request) merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
...@@ -173,6 +187,7 @@ module API ...@@ -173,6 +187,7 @@ module API
success Entities::MergeRequest success Entities::MergeRequest
end end
params do params do
# CE
optional :merge_commit_message, type: String, desc: 'Custom merge commit message' optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :should_remove_source_branch, type: Boolean, optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible' desc: 'When true, the source branch will be deleted if possible'
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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