BigW Consortium Gitlab

Commit 2c643a55 by 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:
key: "ruby-233"
......
......@@ -25,14 +25,20 @@ logs, and code as it's very hard to read otherwise.)
#### Results of GitLab environment info
<details>
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)
(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
</details>
#### Results of GitLab application Check
<details>
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:check SANITIZE=true`)
......@@ -41,6 +47,8 @@ logs, and code as it's very hard to read otherwise.)
(we will only investigate if the tests are passing)
</details>
### Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
......@@ -330,7 +330,7 @@ GEM
grape-entity (0.6.0)
activesupport
multi_json (>= 1.3.2)
grpc (1.1.2)
grpc (1.2.2)
google-protobuf (~> 3.1)
googleauth (~> 0.5.1)
haml (4.0.7)
......
......@@ -50,7 +50,7 @@ window.gl.GfmAutoComplete = {
template: '<li>${title}</li>'
},
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: {
sorter: function(query, items, searchKey) {
......
......@@ -20,57 +20,60 @@ class Issue {
});
Issue.initIssueBtnEventListeners();
}
Issue.$btnNewBranch = $('#new-branch');
Issue.initMergeRequests();
Issue.initRelatedBranches();
Issue.initCanCreateBranch();
}
static initIssueBtnEventListeners() {
var issueFailMessage;
issueFailMessage = 'Unable to update this issue at this time.';
return $('a.btn-close, a.btn-reopen').on('click', function(e) {
var $this, isClose, shouldSubmit, url;
const issueFailMessage = 'Unable to update this issue at this time.';
const closeButtons = $('a.btn-close');
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.stopImmediatePropagation();
$this = $(this);
isClose = $this.hasClass('btn-close');
shouldSubmit = $this.hasClass('btn-comment');
if (shouldSubmit) {
Issue.submitNoteForm($this.closest('form'));
}
$this.prop('disabled', true);
Issue.setNewBranchButtonState(true, null);
url = $this.attr('href');
return $.ajax({
type: 'PUT',
url: url,
error: function(jqXHR, textStatus, errorThrown) {
var issueStatus;
issueStatus = isClose ? 'close' : 'open';
return new Flash(issueFailMessage, 'alert');
},
success: function(data, textStatus, jqXHR) {
if ('id' in data) {
$(document).trigger('issuable:change');
let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
if (isClose) {
$('a.btn-close').addClass('hidden');
$('a.btn-reopen').removeClass('hidden');
$('div.status-box-closed').removeClass('hidden');
$('div.status-box-open').addClass('hidden');
total -= 1;
} else {
$('a.btn-reopen').addClass('hidden');
$('a.btn-close').removeClass('hidden');
$('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);
url: url
}).fail(function(jqXHR, textStatus, errorThrown) {
new Flash(issueFailMessage);
Issue.initCanCreateBranch();
}).done(function(data, textStatus, jqXHR) {
if ('id' in data) {
$(document).trigger('issuable:change');
const isClosed = $this.hasClass('btn-close');
closeButtons.toggleClass('hidden', isClosed);
reopenButtons.toggleClass('hidden', !isClosed);
isClosedBadge.toggleClass('hidden', !isClosed);
isOpenBadge.toggleClass('hidden', isClosed);
let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, ''));
numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
} else {
new Flash(issueFailMessage);
}
$this.prop('disabled', false);
Issue.initCanCreateBranch();
});
});
}
......@@ -86,9 +89,9 @@ class Issue {
static initMergeRequests() {
var $container;
$container = $('#merge-requests');
return $.getJSON($container.data('url')).error(function() {
return new Flash('Failed to load referenced merge requests', 'alert');
}).success(function(data) {
return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load referenced merge requests');
}).done(function(data) {
if ('html' in data) {
return $container.html(data.html);
}
......@@ -98,9 +101,9 @@ class Issue {
static initRelatedBranches() {
var $container;
$container = $('#related-branches');
return $.getJSON($container.data('url')).error(function() {
return new Flash('Failed to load related branches', 'alert');
}).success(function(data) {
return $.getJSON($container.data('url')).fail(function() {
return new Flash('Failed to load related branches');
}).done(function(data) {
if ('html' in data) {
return $container.html(data.html);
}
......@@ -108,24 +111,27 @@ class Issue {
}
static initCanCreateBranch() {
var $container;
$container = $('#new-branch');
// If the user doesn't have the required permissions the container isn't
// rendered at all.
if ($container.length === 0) {
if (Issue.$btnNewBranch.length === 0) {
return;
}
return $.getJSON($container.data('path')).error(function() {
$container.find('.unavailable').show();
return new Flash('Failed to check if a new branch can be created.', 'alert');
}).success(function(data) {
if (data.can_create_branch) {
$container.find('.available').show();
} else {
return $container.find('.unavailable').show();
}
return $.getJSON(Issue.$btnNewBranch.data('path')).fail(function() {
Issue.setNewBranchButtonState(false, false);
new Flash('Failed to check if a new branch can be created.');
}).done(function(data) {
Issue.setNewBranchButtonState(false, data.can_create_branch);
});
}
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;
......@@ -5,7 +5,7 @@
direction: rtl;
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
overflow-x: scroll;
overflow-x: auto;
}
}
......
......@@ -82,7 +82,7 @@
.input-token:last-child {
flex: 1;
-webkit-flex: 1;
max-width: initial;
max-width: inherit;
}
}
......@@ -246,17 +246,17 @@
}
}
.filtered-search-history-dropdown-toggle-button {
.filtered-search-history-dropdown-wrapper {
position: static;
display: flex;
align-items: center;
flex-direction: column;
}
.filtered-search-history-dropdown-toggle-button {
flex: 1;
width: auto;
height: 100%;
padding-top: 0;
padding-left: 0.75em;
padding-bottom: 0;
padding-right: 0.5em;
padding-right: 10px;
background-color: transparent;
border-radius: 0;
border-top: 0;
border-left: 0;
......@@ -264,6 +264,7 @@
border-right: 1px solid $border-color;
color: $gl-text-color-secondary;
line-height: 1;
transition: color 0.1s linear;
......@@ -275,24 +276,21 @@
}
.dropdown-toggle-text {
display: inline-block;
color: inherit;
.fa {
vertical-align: middle;
color: inherit;
}
}
.fa {
position: initial;
position: static;
}
}
.filtered-search-history-dropdown-wrapper {
position: initial;
flex-shrink: 0;
}
.filtered-search-history-dropdown {
width: 40%;
......
......@@ -39,7 +39,7 @@
overflow-y: hidden;
font-size: 12px;
.fa-refresh {
.fa-spinner {
font-size: 24px;
margin-left: 20px;
}
......@@ -219,7 +219,7 @@
font-size: 12px;
position: relative;
.fa-refresh {
.fa-spinner {
font-size: 24px;
}
......@@ -366,7 +366,7 @@
background-color: $row-hover;
}
.fa-refresh {
.fa-spinner {
font-size: 13px;
margin-left: 3px;
}
......
......@@ -10,10 +10,14 @@
position: relative;
&.event-inline {
.profile-icon {
.system-note-image {
top: 20px;
}
.user-avatar {
top: 14px;
}
.event-title,
.event-item-timestamp {
line-height: 40px;
......@@ -24,7 +28,7 @@
color: $gl-text-color;
}
.profile-icon {
.system-note-image {
position: absolute;
left: 0;
top: 14px;
......@@ -35,15 +39,18 @@
fill: $gl-text-color-secondary;
}
&.open-icon svg {
fill: $green-300;
&.opened-icon,
&.created-icon {
svg {
fill: $green-300;
}
}
&.closed-icon svg {
fill: $red-300;
}
&.fork-icon svg {
&.accepted-icon svg {
fill: $blue-300;
}
}
......@@ -128,8 +135,7 @@
li {
&.commit {
background: transparent;
padding: 3px;
padding-left: 0;
padding: 0;
border: none;
.commit-row-title {
......@@ -183,7 +189,7 @@
max-width: 100%;
}
.profile-icon {
.system-note-image {
display: none;
}
......
......@@ -527,6 +527,8 @@
}
.comments-disabled-notif {
line-height: 28px;
.btn {
margin-left: 5px;
}
......
......@@ -18,12 +18,12 @@ ul.notes {
float: left;
svg {
width: 18px;
height: 18px;
width: 16px;
height: 16px;
fill: $gray-darkest;
position: absolute;
left: 30px;
top: 15px;
left: 0;
top: 16px;
}
}
......@@ -144,6 +144,10 @@ ul.notes {
padding: 0;
clear: both;
@media (min-width: $screen-sm-min) {
margin-left: 65px;
}
&.timeline-entry::after {
clear: none;
}
......@@ -172,6 +176,10 @@ ul.notes {
.timeline-content {
padding: 14px 10px;
@media (min-width: $screen-sm-min) {
margin-left: 20px;
}
}
.note-header {
......
......@@ -61,7 +61,6 @@ class Projects::CompareController < Projects::ApplicationController
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
@diff_notes_disabled = true
@grouped_diff_discussions = {}
end
end
......
......@@ -10,7 +10,7 @@ class Projects::HooksController < Projects::ApplicationController
@hook = @project.hooks.new(hook_params)
@hook.save
unless @hook.valid?
unless @hook.valid?
@hooks = @project.hooks.select(&:persisted?)
flash[:alert] = @hook.errors.full_messages.join.html_safe
end
......@@ -49,7 +49,7 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params
params.require(:hook).permit(
:build_events,
:job_events,
:pipeline_events,
:enable_ssl_verification,
:issues_events,
......
......@@ -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_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
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 :close_merge_request_without_source_project, only: [:show, :diffs, :commits, :builds, :pipelines]
before_action :apply_diff_view_cookie!, only: [:new_diffs]
......@@ -39,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@collection_type = "MergeRequest"
@merge_requests = merge_requests_collection
@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)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
......@@ -101,34 +100,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html { define_discussion_vars }
format.json do
@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
define_diff_vars
define_diff_comment_vars
@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") }
end
end
......@@ -140,16 +116,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diff_for_path
if params[:id]
merge_request
define_diff_vars
define_diff_comment_vars
else
build_merge_request
@diffs = @merge_request.diffs(diff_options)
@diff_notes_disabled = true
@grouped_diff_discussions = {}
end
define_commit_vars
render_diff_for_path(@merge_request.diffs(diff_options))
render_diff_for_path(@diffs)
end
def commits
......@@ -586,15 +563,46 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
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
@new_diff_note_attrs = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@diff_notes_disabled = !@merge_request_diff.latest? || @start_sha
@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))
end
......@@ -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
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
if !@merge_request.source_project && @merge_request.open?
@merge_request.close
......
......@@ -62,6 +62,8 @@ module DiffHelper
end
def parallel_diff_discussions(left, right, diff_file)
return unless @grouped_diff_discussions
discussions_left = discussions_right = nil
if left && (left.unchanged? || left.removed?)
......
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)
author = event.author
......@@ -183,4 +194,21 @@ module EventsHelper
"event-inline"
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
......@@ -3,7 +3,8 @@ module JavascriptHelper
javascript_include_tag asset_path(js)
end
def page_specific_javascript_bundle_tag(js)
javascript_include_tag(*webpack_asset_paths(js))
# deprecated; use webpack_bundle_tag directly instead
def page_specific_javascript_bundle_tag(bundle)
webpack_bundle_tag(bundle)
end
end
......@@ -61,12 +61,23 @@ module NotesHelper
end
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, anchor: discussion.line_code)
diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, diff_id: diff_id, anchor: discussion.line_code)
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
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
end
def raw_diffs(*args)
use_gitaly = Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
deltas_only = args.last.is_a?(Hash) && args.last[:deltas_only]
if use_gitaly && !deltas_only
Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
else
raw.diffs(*args)
end
# NOTE: This feature is intentionally disabled until
# https://gitlab.com/gitlab-org/gitaly/issues/178 is resolved
# if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
# Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
# else
raw.diffs(*args)
# end
end
def diffs(diff_options = nil)
......
......@@ -2,11 +2,9 @@
module DiscussionOnDiff
extend ActiveSupport::Concern
included do
NUMBER_OF_TRUNCATED_DIFF_LINES = 16
memoized_values << :active
NUMBER_OF_TRUNCATED_DIFF_LINES = 16
included do
delegate :line_code,
:original_line_code,
:diff_file,
......@@ -29,12 +27,6 @@ module DiscussionOnDiff
true
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
def truncated_diff_lines(highlight: true)
lines = highlight ? highlighted_diff_lines : diff_lines
......
......@@ -25,4 +25,18 @@ module NoteOnDiff
def diff_attributes
raise NotImplementedError
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
......@@ -36,10 +36,10 @@ module Noteable
.discussions(self)
end
def grouped_diff_discussions
def grouped_diff_discussions(*args)
# 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.
notes.inc_relations_for_view.grouped_diff_discussions
notes.inc_relations_for_view.grouped_diff_discussions(*args)
end
def resolvable_discussions
......
......@@ -10,6 +10,7 @@ class DiffDiscussion < Discussion
delegate :position,
:original_position,
:latest_merge_request_diff,
to: :first_note
......
......@@ -65,20 +65,18 @@ class DiffNote < Note
self.position.diff_refs == diff_refs
end
def latest_merge_request_diff
return unless for_merge_request?
self.noteable.merge_request_diff_for(self.position.diff_refs)
end
private
def supported?
for_commit? || self.noteable.has_complete_diff_refs?
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
self.original_position = self.position.dup unless self.original_position&.complete?
end
......
......@@ -21,6 +21,8 @@ class Label < ActiveRecord::Base
has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
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
# Don't allow ',' for label titles
......@@ -193,4 +195,8 @@ class Label < ActiveRecord::Base
def sanitize_title(value)
CGI.unescapeHTML(Sanitize.clean(value.to_s))
end
def strip_whitespace_from_title_and_color
%w(color title).each { |attr| self[attr] = self[attr]&.strip }
end
end
......@@ -7,6 +7,8 @@
class LegacyDiffDiscussion < Discussion
include DiscussionOnDiff
memoized_values << :active
def legacy_diff_discussion?
true
end
......@@ -15,6 +17,12 @@ class LegacyDiffDiscussion < Discussion
LegacyDiffNote
end
def active?(*args)
return @active if @active.present?
@active = first_note.active?(*args)
end
def collapsed?
!active?
end
......
......@@ -56,11 +56,12 @@ class LegacyDiffNote < Note
#
# If the note's current diff cannot be matched in the MergeRequest's current
# diff, it's considered inactive.
def active?
def active?(diff_refs = nil)
return @active if defined?(@active)
return true if for_commit?
return true unless diff_line
return false unless noteable
return false if diff_refs && diff_refs != noteable_diff_refs
noteable_diff = find_noteable_diff
......
......@@ -366,6 +366,14 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff(true)
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
if source_branch_changed? || target_branch_changed?
reload_diff
......
......@@ -31,6 +31,10 @@ class MergeRequestDiff < ActiveRecord::Base
# It allows you to override variables like head_commit_sha before getting diff.
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
select(column_names - ['st_diffs'])
end
......@@ -130,6 +134,12 @@ class MergeRequestDiff < ActiveRecord::Base
st_commits.map { |commit| commit[:id] }
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
return unless start_commit_sha || base_commit_sha
......
......@@ -96,6 +96,7 @@ class Note < ActiveRecord::Base
before_validation :set_discussion_id, on: :create
after_save :keep_around_commit, unless: :for_personal_snippet?
after_save :expire_etag_cache
after_destroy :expire_etag_cache
class << self
def model_name
......@@ -113,11 +114,11 @@ class Note < ActiveRecord::Base
Discussion.build(notes)
end
def grouped_diff_discussions
def grouped_diff_discussions(diff_refs = nil)
diff_notes.
fresh.
discussions.
select(&:active?).
select { |n| n.active?(diff_refs) }.
group_by(&:line_code)
end
......@@ -140,6 +141,10 @@ class Note < ActiveRecord::Base
true
end
def latest_merge_request_diff
nil
end
def max_attachment_size
current_application_settings.max_attachment_size.megabytes.to_i
end
......
......@@ -963,13 +963,15 @@ class Repository
end
def is_ancestor?(ancestor_id, descendant_id)
Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
if is_enabled
raw_repository.is_ancestor?(ancestor_id, descendant_id)
else
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
end
end
# NOTE: This feature is intentionally disabled until
# https://gitlab.com/gitlab-org/gitlab-ce/issues/30586 is resolved
# Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
# if is_enabled
# raw_repository.is_ancestor?(ancestor_id, descendant_id)
# else
merge_base_commit(ancestor_id, descendant_id) == ancestor_id
# end
# end
end
def empty_repo?
......
......@@ -28,6 +28,7 @@ class GroupPolicy < BasePolicy
can! :admin_namespace
can! :admin_group_member
can! :change_visibility_level
can! :create_subgroup if @user.can_create_group
end
if globally_viewable && @subject.request_access_enabled && !member
......
......@@ -13,7 +13,7 @@
= button_to reset_health_check_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset the health check token?' } do
= icon('refresh')
= icon('spinner')
Reset health check access token
%p.light
Health information can be retrieved as plain text, JSON, or XML using:
......
......@@ -21,7 +21,7 @@
= button_to reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset registration token?' } do
= icon('refresh')
= icon('spinner')
Reset runners registration token
.bs-callout
......
......@@ -20,21 +20,22 @@
= discussion.author.to_reference
started a discussion
- url = discussion_diff_path(discussion)
- if discussion.for_commit? && @noteable != discussion.noteable
on
- commit = discussion.noteable
- if commit
commit
- anchor = discussion.line_code if discussion.diff_discussion?
= link_to commit.short_id, namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor), class: 'monospace'
= link_to commit.short_id, url, class: 'monospace'
- else
a deleted commit
- elsif discussion.diff_discussion?
on
- if discussion.active?
= link_to 'the diff', discussion_diff_path(discussion)
- else
an outdated diff
= conditional_link_to url.present?, url do
- if discussion.active?
the diff
- else
an outdated diff
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
= render "discussions/headline", discussion: discussion
......
- if event.target
- 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")
= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
......
.profile-icon.open-icon
= custom_icon("icon_status_open")
= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
......
.profile-icon
= custom_icon("icon_comment_o")
= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
......
- project = event.project
.profile-icon
- if event.action_name == "deleted"
= custom_icon("trash_o")
- else
= custom_icon("icon_commit")
= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
......
......@@ -9,7 +9,7 @@
.nav-controls
= 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
- 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
New Subgroup
......
......@@ -252,7 +252,7 @@
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title
%span Dropdown Title
%span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
......@@ -291,7 +291,7 @@
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading
.dropdown-title
%span Dropdown Title
%span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
......@@ -335,7 +335,7 @@
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user
.dropdown-title
%span Dropdown Title
%span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
......@@ -362,7 +362,7 @@
.dropdown-title
%button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } }
= icon('arrow-left')
%span Dropdown Title
%span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
......
......@@ -28,9 +28,9 @@
= stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print"
= javascript_include_tag(*webpack_asset_paths("runtime"))
= javascript_include_tag(*webpack_asset_paths("common"))
= javascript_include_tag(*webpack_asset_paths("main"))
= webpack_bundle_tag "runtime"
= webpack_bundle_tag "common"
= webpack_bundle_tag "main"
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
......
......@@ -25,13 +25,6 @@
#blob-content-holder.blob-content-holder
%article.file-holder
= 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?
.file-content.code
......
......@@ -38,3 +38,10 @@
- if current_user
= replace_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 @@
- else
= build.id
- 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
new Sidebar();
......@@ -36,7 +36,7 @@
= icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.')
- 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
- if job.tags.any?
......
......@@ -5,8 +5,7 @@
- left = line[:left]
- right = line[: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
- if left
- case left.type
......
......@@ -4,11 +4,10 @@
%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' : '' }
- discussions = @grouped_diff_discussions unless @diff_notes_disabled
= render partial: "projects/diffs/line",
collection: diff_file.highlighted_diff_lines,
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?
- last_line = diff_file.highlighted_diff_lines.last
......
......@@ -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
new commits
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
= icon('info-circle')
- if @start_sha
Comments are disabled because you're comparing two versions of this merge request.
- else
Comments are disabled because you're viewing an old version of this merge request.
= link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
Discussions on this version of the merge request are displayed but comment creation is disabled.
.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
include BuildQueue
def perform(build_id)
Ci::Build.find_by(id: build_id)
.try(:update_coverage)
Ci::Build.find_by(id: build_id)&.update_coverage
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:
storages:
default:
path: tmp/tests/repositories/
gitaly_address: unix:<%= Rails.root.join('tmp/sockets/private/gitaly.socket') %>
gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
gitaly:
enabled: false
enabled: true
backup:
path: tmp/tests/backups
gitlab_shell:
......
......@@ -11,6 +11,7 @@ var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeMod
var ROOT_PATH = path.resolve(__dirname, '..');
var IS_PRODUCTION = process.env.NODE_ENV === 'production';
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_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
var WEBPACK_REPORT = process.env.WEBPACK_REPORT;
......@@ -182,12 +183,13 @@ if (IS_PRODUCTION) {
if (IS_DEV_SERVER) {
config.devtool = 'cheap-module-eval-source-map';
config.devServer = {
host: DEV_SERVER_HOST,
port: DEV_SERVER_PORT,
headers: { 'Access-Control-Allow-Origin': '*' },
stats: 'errors-only',
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(
// watch node_modules for changes if we encounter a missing module compile error
new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
......
# rubocop:disable all
class ConvertClosedToStateInIssue < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}"
......
# rubocop:disable all
class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
execute "UPDATE #{table_name} SET state = 'merged' WHERE closed = #{true_value} AND merged = #{true_value}"
......
# rubocop:disable all
class ConvertClosedToStateInMilestone < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}"
......
# rubocop:disable all
class UserColorScheme < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
add_column :users, :color_scheme_id, :integer, null: false, default: 1
......
# rubocop:disable all
class AddVisibilityLevelToProjects < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def self.up
add_column :projects, :visibility_level, :integer, :default => 0, :null => false
......
# rubocop:disable all
class MigrateAlreadyImportedProjects < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
execute("UPDATE projects SET import_status = 'finished' WHERE imported = #{true_value}")
......
# rubocop:disable all
class AddVisibilityLevelToSnippet < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
add_column :snippets, :visibility_level, :integer, :default => 0, :null => false
......
# rubocop:disable all
class MigrateCiWebHooks < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
execute(
......
# rubocop:disable all
class MigrateCiEmails < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
# This inserts a new service: BuildsEmailService
......
# rubocop:disable all
class MigrateCiSlackService < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
properties_query = 'SELECT properties FROM ci_services ' \
......
# rubocop:disable all
class MigrateCiHipChatService < ActiveRecord::Migration
include Gitlab::Database
include Gitlab::Database::MigrationHelpers
def up
# From properties strip `hipchat_` key
......
class MigrateBuildEventsToPipelineEvents < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
include Gitlab::Database
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`:
```haml
- content_for :page_specific_javascripts do
= page_specific_javascript_bundle_tag('lib_chart')
= page_specific_javascript_bundle_tag('graphs')
= webpack_bundle_tag 'lib_chart'
= webpack_bundle_tag 'graphs'
```
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.
## Karma test suite
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
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
`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
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.
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
JavaScript enabled:
```ruby
# For one spec
it 'presents information about abuse report', js: true do
# assertions...
it 'presents information about abuse report', :js do
# assertions...
end
describe "Admin::AbuseReports", js: true do
it 'presents information about abuse report' do
# assertions...
end
it 'shows buttons for adding to abuse report' do
# assertions...
end
describe "Admin::AbuseReports", :js do
it 'presents information about abuse report' do
# assertions...
end
it 'shows buttons for adding to abuse report' do
# assertions...
end
end
```
......@@ -113,13 +121,12 @@ file for the failing spec, add the `@javascript` flag above the Scenario:
```
@javascript
Scenario: Developer can approve merge request
Given I am a "Shop" developer
And I visit project "Shop" merge requests page
And merge request 'Bug NS-04' must be approved
And I click link "Bug NS-04"
When I click link "Approve"
Then I should see approved merge request "Bug NS-04"
Given I am a "Shop" developer
And I visit project "Shop" merge requests page
And merge request 'Bug NS-04' must be approved
And I click link "Bug NS-04"
When I click link "Approve"
Then I should see approved merge request "Bug NS-04"
```
[capybara]: http://teamcapybara.github.io/capybara/
......
......@@ -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
many years of data in their database.
In addition, having to take a server offline for a an upgrade small or big is
a 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.
In addition, having to take a server offline for a a upgrade small or big is a
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 should not require GitLab installations to be taken offline unless
_absolutely_ necessary - see the ["What Requires Downtime?"](what_requires_downtime.md)
page. If a migration requires downtime, this should be clearly mentioned during
the review process, as well as being documented in the monthly release post. For
more information, see the "Downtime Tagging" section below.
Migrations are **not** allowed to require GitLab installations to be taken
offline unless _absolutely necessary_. Downtime assumptions should be based on
the behaviour of a migration when performed using PostgreSQL, as various
operations in MySQL may require downtime without there being alternatives.
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
or inconsistencies and guard for that. Try to make as little assumptions as possible
about the state of the database.
or inconsistencies and guard for that. Try to make as few assumptions as
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.
If needed copy-paste GitLab code into the migration to make it forward compatible.
Each migration **must** be added in its own commit with a descriptive commit
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
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
following two constants to the migration class' body:
require downtime it must also specify a reason for this. This is required even
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.
......@@ -50,12 +75,53 @@ from a migration class.
## 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.
In your migration, add a comment describing how the reversibility of the
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
When removing an index make sure to use the method `remove_concurrent_index` instead
......@@ -78,7 +144,10 @@ end
## 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
of the regular `add_index` method. The `add_concurrent_index` method
......@@ -90,17 +159,22 @@ so:
```ruby
class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
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
```
## 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
requiring downtime. This method is not reversible so you must manually define
the `up` and `down` methods in your migration class.
......@@ -123,6 +197,9 @@ class MyMigration < ActiveRecord::Migration
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
......@@ -147,13 +224,15 @@ add_column(:projects, :foo, :integer, default: 10, limit: 8)
## 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.
## 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:
......@@ -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(",")})")
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.
......@@ -289,9 +289,9 @@ sudo usermod -aG redis git
### Clone the Source
# 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
......@@ -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
# If you are using non-default settings you need to update config.toml
sudo -u git -H editor config.toml
# Enable Gitaly in the init script
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
through our regular docs, and, when available, through articles (guides,
tutorials, technical overviews, blog posts) and videos.
- [GitLab Installation](../install/README.md)
- [Authentication](authentication/index.md)
- [Continuous Integration (GitLab CI)](../ci/README.md)
- [Git](git/index.md)
- [GitLab Installation](../install/README.md)
- [GitLab Pages](../user/project/pages/index.md)
>**Note:**
Non-linked topics are currently under development and subjected to change.
More topics will be available soon.
>**Note:** More topics will be available soon.
# 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
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
......
......@@ -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
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
GitLab comes in two flavors: [Community Edition][ce] which is MIT licensed,
......
......@@ -5,10 +5,10 @@
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
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.
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.
## Overview
......
......@@ -60,7 +60,7 @@ to use terminals. Support is currently limited to the first container in the
first pod of your environment.
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
containers. To use this integration, you should deploy to Kubernetes using
the deployment variables above, ensuring any pods you create are labelled with
......
......@@ -5,11 +5,16 @@ module API
before { authenticate! }
helpers do
params :optional_params do
params :optional_params_ce do
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 :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 :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
params :statistics_params do
......
......@@ -30,7 +30,7 @@ module API
use :pagination
end
params :issue_params do
params :issue_params_ce do
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 :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
......@@ -38,6 +38,10 @@ module API
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'
end
params :issue_params do
use :issue_params_ce
end
end
resource :issues do
......
......@@ -33,13 +33,17 @@ module API
end
end
params :optional_params do
params :optional_params_ce do
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 :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 :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
end
params :optional_params do
use :optional_params_ce
end
end
desc 'List merge requests' do
......@@ -145,14 +149,24 @@ module API
success Entities::MergeRequest
end
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 :target_branch, type: String, allow_blank: false, desc: 'The target branch'
optional :state_event, type: String, values: %w[close reopen],
desc: 'Status of the merge request'
use :optional_params
at_least_one_of :title, :target_branch, :description, :assignee_id,
:milestone_id, :labels, :state_event,
:remove_source_branch
at_least_one_of(*at_least_one_of_ce)
end
put ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
......@@ -173,6 +187,7 @@ module API
success Entities::MergeRequest
end
params do
# CE
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible'
......
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