Commit 6625f479 authored by Regis's avatar Regis

Merge branch 'master' into auto-pipelines-vue

parents 55df5536 6c624821
...@@ -2,6 +2,26 @@ ...@@ -2,6 +2,26 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 8.15.3 (2017-01-06)
- Rename wiki_events to wiki_page_events in project hooks API to avoid errors. !0 (8425)
- Rename projects wth reserved names. !8234
- Cache project authorizations even when user has access to zero projects. !8327
- Fix a minor grammar error in merge request widget. !8337
- Fix unclear closing issue behaviour on Merge Request show page. !8345 (Gabriel Gizotti)
- fix border in login session tabs. !8346
- Copy, don't move uploaded avatar files. !8396
- Increases width of mini-pipeline-graph dropdown to prevent wrong position on chrome on ubuntu. !8399
- Removes invalid html and unneed CSS to prevent shaking in the pipelines tab. !8411
- Gitlab::LDAP::Person uses LDAP attributes configuration. !8418
- Fix 500 errors when creating a user with identity via API. !8442
- Whitelist next project names: assets, profile, public. !8470
- Fixed regression of note-headline-light where it was always placed on 2 lines, even on wide viewports.
- Fix 500 error when visit group from admin area if group name contains dot.
- Fix cross-project references copy to include the project reference.
- Fix 500 error renaming group.
- Fixed GFM dropdown not showing on new lines.
## 8.15.2 (2016-12-27) ## 8.15.2 (2016-12-27)
- Fix finding the latest pipeline. !8301 - Fix finding the latest pipeline. !8301
......
...@@ -84,10 +84,14 @@ gem 'dropzonejs-rails', '~> 0.7.1' ...@@ -84,10 +84,14 @@ gem 'dropzonejs-rails', '~> 0.7.1'
# for backups # for backups
gem 'fog-aws', '~> 0.9' gem 'fog-aws', '~> 0.9'
gem 'fog-core', '~> 1.40' gem 'fog-core', '~> 1.40'
gem 'fog-google', '~> 0.5'
gem 'fog-local', '~> 0.3' gem 'fog-local', '~> 0.3'
gem 'fog-openstack', '~> 0.1' gem 'fog-openstack', '~> 0.1'
gem 'fog-rackspace', '~> 0.1.1' gem 'fog-rackspace', '~> 0.1.1'
# for Google storage
gem 'google-api-client', '~> 0.8.6'
# for aws storage # for aws storage
gem 'unf', '~> 0.1.4' gem 'unf', '~> 0.1.4'
......
...@@ -58,6 +58,10 @@ GEM ...@@ -58,6 +58,10 @@ GEM
attr_encrypted (3.0.3) attr_encrypted (3.0.3)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.0.0) attr_required (1.0.0)
autoparse (0.3.3)
addressable (>= 2.3.1)
extlib (>= 0.9.15)
multi_json (>= 1.0.0)
autoprefixer-rails (6.2.3) autoprefixer-rails (6.2.3)
execjs execjs
json json
...@@ -179,6 +183,7 @@ GEM ...@@ -179,6 +183,7 @@ GEM
excon (0.52.0) excon (0.52.0)
execjs (2.6.0) execjs (2.6.0)
expression_parser (0.9.0) expression_parser (0.9.0)
extlib (0.9.16)
factory_girl (4.7.0) factory_girl (4.7.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
factory_girl_rails (4.7.0) factory_girl_rails (4.7.0)
...@@ -208,6 +213,10 @@ GEM ...@@ -208,6 +213,10 @@ GEM
builder builder
excon (~> 0.49) excon (~> 0.49)
formatador (~> 0.2) formatador (~> 0.2)
fog-google (0.5.0)
fog-core
fog-json
fog-xml
fog-json (1.0.2) fog-json (1.0.2)
fog-core (~> 1.0) fog-core (~> 1.0)
multi_json (~> 1.10) multi_json (~> 1.10)
...@@ -279,6 +288,25 @@ GEM ...@@ -279,6 +288,25 @@ GEM
json json
multi_json multi_json
request_store (>= 1.0) request_store (>= 1.0)
google-api-client (0.8.7)
activesupport (>= 3.2, < 5.0)
addressable (~> 2.3)
autoparse (~> 0.3)
extlib (~> 0.9)
faraday (~> 0.9)
googleauth (~> 0.3)
launchy (~> 2.4)
multi_json (~> 1.10)
retriable (~> 1.4)
signet (~> 0.6)
googleauth (0.5.1)
faraday (~> 0.9)
jwt (~> 1.4)
logging (~> 2.0)
memoist (~> 0.12)
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
grape (0.18.0) grape (0.18.0)
activesupport activesupport
builder builder
...@@ -381,11 +409,16 @@ GEM ...@@ -381,11 +409,16 @@ GEM
listen (3.0.5) listen (3.0.5)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
rb-inotify (>= 0.9) rb-inotify (>= 0.9)
little-plugger (1.1.4)
logging (2.1.0)
little-plugger (~> 1.1)
multi_json (~> 1.10)
loofah (2.0.3) loofah (2.0.3)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.6.4) mail (2.6.4)
mime-types (>= 1.16, < 4) mime-types (>= 1.16, < 4)
mail_room (0.9.0) mail_room (0.9.0)
memoist (0.15.0)
method_source (0.8.2) method_source (0.8.2)
mime-types (2.99.3) mime-types (2.99.3)
mimemagic (0.3.0) mimemagic (0.3.0)
...@@ -473,6 +506,7 @@ GEM ...@@ -473,6 +506,7 @@ GEM
org-ruby (0.9.12) org-ruby (0.9.12)
rubypants (~> 0.2) rubypants (~> 0.2)
orm_adapter (0.5.0) orm_adapter (0.5.0)
os (0.9.6)
paranoia (2.2.0) paranoia (2.2.0)
activerecord (>= 4.0, < 5.1) activerecord (>= 4.0, < 5.1)
parser (2.3.1.4) parser (2.3.1.4)
...@@ -584,6 +618,7 @@ GEM ...@@ -584,6 +618,7 @@ GEM
http-cookie (>= 1.0.2, < 2.0) http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0) mime-types (>= 1.16, < 4.0)
netrc (~> 0.8) netrc (~> 0.8)
retriable (1.4.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (2.0.7) rouge (2.0.7)
...@@ -675,6 +710,11 @@ GEM ...@@ -675,6 +710,11 @@ GEM
sidekiq (>= 4.2.1) sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0) sidekiq-limit_fetch (3.4.0)
sidekiq (>= 4) sidekiq (>= 4)
signet (0.7.3)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (~> 1.5)
multi_json (~> 1.10)
simplecov (0.12.0) simplecov (0.12.0)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
...@@ -841,6 +881,7 @@ DEPENDENCIES ...@@ -841,6 +881,7 @@ DEPENDENCIES
flay (~> 2.6.1) flay (~> 2.6.1)
fog-aws (~> 0.9) fog-aws (~> 0.9)
fog-core (~> 1.40) fog-core (~> 1.40)
fog-google (~> 0.5)
fog-local (~> 0.3) fog-local (~> 0.3)
fog-openstack (~> 0.1) fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1) fog-rackspace (~> 0.1.1)
...@@ -856,6 +897,7 @@ DEPENDENCIES ...@@ -856,6 +897,7 @@ DEPENDENCIES
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.2)
gon (~> 6.1.0) gon (~> 6.1.0)
google-api-client (~> 0.8.6)
grape (~> 0.18.0) grape (~> 0.18.0)
grape-entity (~> 0.6.0) grape-entity (~> 0.6.0)
haml_lint (~> 0.18.2) haml_lint (~> 0.18.2)
......
...@@ -45,14 +45,28 @@ ...@@ -45,14 +45,28 @@
const issue = this.list.findIssue(this.detailIssue.issue.id); const issue = this.list.findIssue(this.detailIssue.issue.id);
if (issue) { if (issue) {
const offsetLeft = this.$el.offsetLeft;
const boardsList = document.querySelectorAll('.boards-list')[0]; const boardsList = document.querySelectorAll('.boards-list')[0];
const right = (this.$el.offsetLeft + this.$el.offsetWidth) - boardsList.offsetWidth; const left = boardsList.scrollLeft - offsetLeft;
const left = boardsList.scrollLeft - this.$el.offsetLeft; let right = (offsetLeft + this.$el.offsetWidth);
if (window.innerWidth > 768 && boardsList.classList.contains('is-compact')) {
// -290 here because width of boardsList is animating so therefore
// getting the width here is incorrect
// 290 is the width of the sidebar
right -= (boardsList.offsetWidth - 290);
} else {
right -= boardsList.offsetWidth;
}
if (right - boardsList.scrollLeft > 0) { if (right - boardsList.scrollLeft > 0) {
boardsList.scrollLeft = right; $(boardsList).animate({
scrollLeft: right
}, this.sortableOptions.animation);
} else if (left > 0) { } else if (left > 0) {
boardsList.scrollLeft = this.$el.offsetLeft; $(boardsList).animate({
scrollLeft: offsetLeft
}, this.sortableOptions.animation);
} }
} }
}, },
...@@ -65,7 +79,7 @@ ...@@ -65,7 +79,7 @@
} }
}, },
mounted () { mounted () {
const options = gl.issueBoards.getBoardSortableDefaultOptions({ this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
disabled: this.disabled, disabled: this.disabled,
group: 'boards', group: 'boards',
draggable: '.is-draggable', draggable: '.is-draggable',
...@@ -84,7 +98,7 @@ ...@@ -84,7 +98,7 @@
} }
}); });
this.sortable = Sortable.create(this.$el.parentNode, options); this.sortable = Sortable.create(this.$el.parentNode, this.sortableOptions);
}, },
}); });
})(); })();
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
gl.issueBoards.getBoardSortableDefaultOptions = (obj) => { gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
let defaultSortOptions = { let defaultSortOptions = {
animation: 200,
forceFallback: true, forceFallback: true,
fallbackClass: 'is-dragging', fallbackClass: 'is-dragging',
fallbackOnBody: true, fallbackOnBody: true,
......
...@@ -210,7 +210,9 @@ ...@@ -210,7 +210,9 @@
new gl.Members(); new gl.Members();
new UsersSelect(); new UsersSelect();
break; break;
case 'projects:project_members:index': case 'projects:members:show':
new gl.MemberExpirationDate('.js-access-expiration-date-groups');
new GroupsSelect();
new gl.MemberExpirationDate(); new gl.MemberExpirationDate();
new gl.Members(); new gl.Members();
new UsersSelect(); new UsersSelect();
...@@ -256,10 +258,6 @@ ...@@ -256,10 +258,6 @@
case 'projects:artifacts:browse': case 'projects:artifacts:browse':
new BuildArtifacts(); new BuildArtifacts();
break; break;
case 'projects:group_links:index':
new gl.MemberExpirationDate();
new GroupsSelect();
break;
case 'search:show': case 'search:show':
new Search(); new Search();
break; break;
......
...@@ -80,9 +80,12 @@ ...@@ -80,9 +80,12 @@
} }
parseSelectedDate() { parseSelectedDate() {
this.rawSelectedDate = $("input[name='" + this.fieldName + "']").val(); this.rawSelectedDate = $(`input[name='${this.fieldName}']`).val();
if (this.rawSelectedDate.length) { if (this.rawSelectedDate.length) {
let dateObj = new Date(this.rawSelectedDate); // Construct Date object manually to avoid buggy dateString support within Date constructor
const dateArray = this.rawSelectedDate.split('-').map(v => parseInt(v, 10));
const dateObj = new Date(dateArray[0], dateArray[1] - 1, dateArray[2]);
this.displayedDate = $.datepicker.formatDate('M d, yy', dateObj); this.displayedDate = $.datepicker.formatDate('M d, yy', dateObj);
} else { } else {
this.displayedDate = 'No due date'; this.displayedDate = 'No due date';
......
...@@ -216,7 +216,7 @@ ...@@ -216,7 +216,7 @@
<th class="environments-deploy">Last deployment</th> <th class="environments-deploy">Last deployment</th>
<th class="environments-build">Build</th> <th class="environments-build">Build</th>
<th class="environments-commit">Commit</th> <th class="environments-commit">Commit</th>
<th class="environments-date"></th> <th class="environments-date">Created</th>
<th class="hidden-xs environments-actions"></th> <th class="hidden-xs environments-actions"></th>
</tr> </tr>
</thead> </thead>
......
...@@ -44,9 +44,25 @@ ...@@ -44,9 +44,25 @@
} }
}; };
gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) { gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
var insertText, inserted, selectedSplit, startChar; var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine;
removedLastNewLine = false;
removedFirstNewLine = false;
// Remove the first newline
if (selected.indexOf('\n') === 0) {
removedFirstNewLine = true;
selected = selected.replace(/\n+/, '');
}
// Remove the last newline
if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\n$/, '').length) {
removedLastNewLine = true;
selected = selected.replace(/\n$/, '');
}
selectedSplit = selected.split('\n'); selectedSplit = selected.split('\n');
startChar = !wrap && textArea.selectionStart > 0 ? '\n' : ''; startChar = !wrap && textArea.selectionStart > 0 ? '\n' : '';
if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) { if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) {
if (blockTag != null) { if (blockTag != null) {
insertText = this.blockTagText(text, textArea, blockTag, selected); insertText = this.blockTagText(text, textArea, blockTag, selected);
...@@ -62,6 +78,15 @@ ...@@ -62,6 +78,15 @@
} else { } else {
insertText = "" + startChar + tag + selected + (wrap ? tag : ' '); insertText = "" + startChar + tag + selected + (wrap ? tag : ' ');
} }
if (removedFirstNewLine) {
insertText = '\n' + insertText;
}
if (removedLastNewLine) {
insertText += '\n';
}
if (document.queryCommandSupported('insertText')) { if (document.queryCommandSupported('insertText')) {
inserted = document.execCommand('insertText', false, insertText); inserted = document.execCommand('insertText', false, insertText);
} }
...@@ -74,9 +99,9 @@ ...@@ -74,9 +99,9 @@
document.execCommand("ms-endUndoUnit"); document.execCommand("ms-endUndoUnit");
} catch (error) {} } catch (error) {}
} }
return this.moveCursor(textArea, tag, wrap); return this.moveCursor(textArea, tag, wrap, removedLastNewLine);
}; };
gl.text.moveCursor = function(textArea, tag, wrapped) { gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) {
var pos; var pos;
if (!textArea.setSelectionRange) { if (!textArea.setSelectionRange) {
return; return;
...@@ -87,6 +112,11 @@ ...@@ -87,6 +112,11 @@
} else { } else {
pos = textArea.selectionStart; pos = textArea.selectionStart;
} }
if (removedLastNewLine) {
pos -= 1;
}
return textArea.setSelectionRange(pos, pos); return textArea.setSelectionRange(pos, pos);
} }
}; };
......
/* eslint-disable func-names, space-before-function-paren, vars-on-top, no-var, object-shorthand, comma-dangle, max-len */ (() => {
(function() {
// Add datepickers to all `js-access-expiration-date` elements. If those elements are // Add datepickers to all `js-access-expiration-date` elements. If those elements are
// children of an element with the `clearable-input` class, and have a sibling // children of an element with the `clearable-input` class, and have a sibling
// `js-clear-input` element, then show that element when there is a value in the // `js-clear-input` element, then show that element when there is a value in the
// datepicker, and make clicking on that element clear the field. // datepicker, and make clicking on that element clear the field.
// //
gl.MemberExpirationDate = function() { window.gl = window.gl || {};
gl.MemberExpirationDate = (selector = '.js-access-expiration-date') => {
function toggleClearInput() { function toggleClearInput() {
$(this).closest('.clearable-input').toggleClass('has-value', $(this).val() !== ''); $(this).closest('.clearable-input').toggleClass('has-value', $(this).val() !== '');
} }
const inputs = $(selector);
var inputs = $('.js-access-expiration-date');
inputs.datepicker({ inputs.datepicker({
dateFormat: 'yy-mm-dd', dateFormat: 'yy-mm-dd',
minDate: 1, minDate: 1,
onSelect: function () { onSelect: function onSelect() {
$(this).trigger('change'); $(this).trigger('change');
toggleClearInput.call(this); toggleClearInput.call(this);
} },
}); });
inputs.next('.js-clear-input').on('click', function(event) { inputs.next('.js-clear-input').on('click', function clicked(event) {
event.preventDefault(); event.preventDefault();
var input = $(this).closest('.clearable-input').find('.js-access-expiration-date'); const input = $(this).closest('.clearable-input').find(selector);
input.datepicker('setDate', null) input.datepicker('setDate', null)
.trigger('change'); .trigger('change');
toggleClearInput.call(input); toggleClearInput.call(input);
......
...@@ -895,7 +895,9 @@ ...@@ -895,7 +895,9 @@
new GLForm($editForm.find('form')); new GLForm($editForm.find('form'));
$editForm.find('form').attr('action', postUrl); $editForm.find('form')
.attr('action', postUrl)
.attr('data-remote', 'true');
$editForm.find('.js-form-target-id').val(targetId); $editForm.find('.js-form-target-id').val(targetId);
$editForm.find('.js-form-target-type').val(targetType); $editForm.find('.js-form-target-type').val(targetType);
$editForm.find('.js-note-text').focus().val(originalContent); $editForm.find('.js-note-text').focus().val(originalContent);
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
}); });
$('.no-template', this.dropdown.parent()).on('click', () => { $('.no-template', this.dropdown.parent()).on('click', () => {
this.currentTemplate = ''; this.currentTemplate.content = '';
this.setInputValueToTemplateContent(); this.setInputValueToTemplateContent();
$('.dropdown-toggle-text', this.dropdown).text('Choose a template'); $('.dropdown-toggle-text', this.dropdown).text('Choose a template');
}); });
......
...@@ -163,6 +163,10 @@ ul.content-list { ...@@ -163,6 +163,10 @@ ul.content-list {
&:last-child { &:last-child {
margin-right: 0; margin-right: 0;
@media(max-width: $screen-xs-max) {
margin: 0 auto;
}
} }
} }
......
...@@ -74,6 +74,7 @@ ...@@ -74,6 +74,7 @@
height: 475px; // Needed for PhantomJS height: 475px; // Needed for PhantomJS
height: calc(100vh - 220px); height: calc(100vh - 220px);
min-height: 475px; min-height: 475px;
transition: width .2s;
&.is-compact { &.is-compact {
width: calc(100% - 290px); width: calc(100% - 290px);
...@@ -338,3 +339,18 @@ ...@@ -338,3 +339,18 @@
} }
} }
} }
.right-sidebar.right-sidebar-expanded {
&.boards-sidebar-slide-enter-active,
&.boards-sidebar-slide-leave-active {
transition: width .2s,
padding .2s;
}
&.boards-sidebar-slide-enter,
&.boards-sidebar-slide-leave-active {
width: 0;
padding-left: 0;
padding-right: 0;
}
}
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
} }
.form-horizontal { .form-horizontal {
margin-top: 5px; margin-top: 20px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
display: -webkit-flex; display: -webkit-flex;
...@@ -98,6 +98,10 @@ ...@@ -98,6 +98,10 @@
padding-right: 35px; padding-right: 35px;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
width: 250px;
}
@media (min-width: $screen-md-min) {
width: 350px; width: 350px;
} }
......
module GlobalMilestones
extend ActiveSupport::Concern
def milestones
epoch = DateTime.parse('1970-01-01')
@milestones = MilestonesFinder.new.execute(@projects, params)
@milestones = GlobalMilestone.build_collection(@milestones)
@milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date }
end
def milestone
milestones = Milestone.of_projects(@projects).where(title: params[:title])
if milestones.present?
@milestone = GlobalMilestone.new(params[:title], milestones)
else
render_404
end
end
end
class Dashboard::MilestonesController < Dashboard::ApplicationController class Dashboard::MilestonesController < Dashboard::ApplicationController
include GlobalMilestones
before_action :projects before_action :projects
before_action :milestone, only: [:show] before_action :milestone, only: [:show]
...@@ -17,4 +15,15 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController ...@@ -17,4 +15,15 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
def show def show
end end
private
def milestones
@milestones = GlobalMilestone.build_collection(@projects, params)
end
def milestone
@milestone = GlobalMilestone.build(@projects, params[:title])
render_404 unless @milestone
end
end end
class Groups::MilestonesController < Groups::ApplicationController class Groups::MilestonesController < Groups::ApplicationController
include GlobalMilestones
before_action :group_projects before_action :group_projects
before_action :milestone, only: [:show, :update] before_action :milestone, only: [:show, :update]
before_action :authorize_admin_milestones!, only: [:new, :create, :update] before_action :authorize_admin_milestones!, only: [:new, :create, :update]
...@@ -73,4 +71,13 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -73,4 +71,13 @@ class Groups::MilestonesController < Groups::ApplicationController
def milestone_path(title) def milestone_path(title)
group_milestone_path(@group, title.to_slug.to_s, title: title) group_milestone_path(@group, title.to_slug.to_s, title: title)
end end
def milestones
@milestones = GroupMilestone.build_collection(@group, @projects, params)
end
def milestone
@milestone = GroupMilestone.build(@group, @projects, params[:title])
render_404 unless @milestone
end
end end
...@@ -4,10 +4,7 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -4,10 +4,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
before_action :authorize_admin_project_member!, only: [:update] before_action :authorize_admin_project_member!, only: [:update]
def index def index
@group_links = project.project_group_links.all redirect_to namespace_project_settings_members_path
@skip_groups = @group_links.pluck(:group_id)
@skip_groups << project.namespace_id unless project.personal?
end end
def create def create
...@@ -25,7 +22,7 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -25,7 +22,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
flash[:alert] = 'Please select a group.' flash[:alert] = 'Please select a group.'
end end
redirect_to namespace_project_group_links_path(project.namespace, project) redirect_to namespace_project_settings_members_path(project.namespace, project)
end end
def update def update
...@@ -39,7 +36,7 @@ class Projects::GroupLinksController < Projects::ApplicationController ...@@ -39,7 +36,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_to namespace_project_group_links_path(project.namespace, project) redirect_to namespace_project_settings_members_path(project.namespace, project)
end end
format.js { head :ok } format.js { head :ok }
end end
......
...@@ -6,54 +6,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -6,54 +6,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController
before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access] before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access]
def index def index
@sort = params[:sort].presence || sort_value_name sort = params[:sort].presence || sort_value_name
@group_links = @project.project_group_links redirect_to namespace_project_settings_members_path(@project.namespace, @project, sort: sort)
@project_members = @project.project_members
@project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project)
group = @project.group
if group
# We need `.where.not(user_id: nil)` here otherwise when a group has an
# invitee, it would make the following query return 0 rows since a NULL
# user_id would be present in the subquery
# See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values
# FIXME: This whole logic should be moved to a finder!
non_null_user_ids = @project_members.where.not(user_id: nil).select(:user_id)
group_members = group.group_members.where.not(user_id: non_null_user_ids)
group_members = group_members.non_invite unless can?(current_user, :admin_group, @group)
end
if params[:search].present?
user_ids = @project.users.search(params[:search]).select(:id)
@project_members = @project_members.where(user_id: user_ids)
if group_members
user_ids = group.users.search(params[:search]).select(:id)
group_members = group_members.where(user_id: user_ids)
end
@group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id))
end
wheres = ["members.id IN (#{@project_members.select(:id).to_sql})"]
wheres << "members.id IN (#{group_members.select(:id).to_sql})" if group_members
@project_members = Member.
where(wheres.join(' OR ')).
sort(@sort).
page(params[:page])
@requesters = AccessRequestsFinder.new(@project).execute(current_user)
@project_member = @project.project_members.new
end end
def create def create
status = Members::CreateService.new(@project, current_user, params).execute status = Members::CreateService.new(@project, current_user, params).execute
redirect_url = namespace_project_project_members_path(@project.namespace, @project) redirect_url = namespace_project_settings_members_path(@project.namespace, @project)
if status if status
redirect_to redirect_url, notice: 'Users were successfully added.' redirect_to redirect_url, notice: 'Users were successfully added.'
...@@ -76,14 +36,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -76,14 +36,14 @@ class Projects::ProjectMembersController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
redirect_to namespace_project_project_members_path(@project.namespace, @project) redirect_to namespace_project_settings_members_path(@project.namespace, @project)
end end
format.js { head :ok } format.js { head :ok }
end end
end end
def resend_invite def resend_invite
redirect_path = namespace_project_project_members_path(@project.namespace, @project) redirect_path = namespace_project_settings_members_path(@project.namespace, @project)
@project_member = @project.project_members.find(params[:id]) @project_member = @project.project_members.find(params[:id])
...@@ -106,7 +66,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -106,7 +66,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
return render_404 return render_404
end end
redirect_to(namespace_project_project_members_path(project.namespace, project), redirect_to(namespace_project_settings_members_path(project.namespace, project),
notice: notice) notice: notice)
end end
......
module Projects
module Settings
class MembersController < Projects::ApplicationController
include SortingHelper
def show
@sort = params[:sort].presence || sort_value_name
@group_links = @project.project_group_links
@project_members = @project.project_members
@project_members = @project_members.non_invite unless can?(current_user, :admin_project, @project)
group = @project.group
# group links
@group_links = @project.project_group_links.all
@skip_groups = @group_links.pluck(:group_id)
@skip_groups << @project.namespace_id unless @project.personal?
if group
# We need `.where.not(user_id: nil)` here otherwise when a group has an
# invitee, it would make the following query return 0 rows since a NULL
# user_id would be present in the subquery
# See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values
group_members = MembersFinder.new(@project_members, group).execute(current_user)
end
if params[:search].present?
user_ids = @project.users.search(params[:search]).select(:id)
@project_members = @project_members.where(user_id: user_ids)
if group_members
user_ids = group.users.search(params[:search]).select(:id)
group_members = group_members.where(user_id: user_ids)
end
@group_links = @project.project_group_links.where(group_id: @project.invited_groups.search(params[:search]).select(:id))
end
wheres = ["members.id IN (#{@project_members.select(:id).to_sql})"]
wheres << "members.id IN (#{group_members.select(:id).to_sql})" if group_members
@project_members = Member.
where(wheres.join(' OR ')).
sort(@sort).
page(params[:page])
@requesters = AccessRequestsFinder.new(@project).execute(current_user)
@project_member = @project.project_members.new
end
end
end
end
class MembersFinder < Projects::ApplicationController
def initialize(project_members, project_group)
@project_members = project_members
@project_group = project_group
end
def execute(current_user)
non_null_user_ids = @project_members.where.not(user_id: nil).select(:user_id)
group_members = @project_group.group_members.where.not(user_id: non_null_user_ids)
group_members = group_members.non_invite unless can?(current_user, :admin_group, @project_group)
group_members
end
end
...@@ -206,4 +206,9 @@ module GitlabRoutingHelper ...@@ -206,4 +206,9 @@ module GitlabRoutingHelper
file_namespace_project_build_artifacts_path(*args) file_namespace_project_build_artifacts_path(*args)
end end
end end
# Settings
def project_settings_members_path(project, *args)
namespace_project_settings_members_path(project.namespace, project, *args)
end
end end
...@@ -39,7 +39,7 @@ module SearchHelper ...@@ -39,7 +39,7 @@ module SearchHelper
# Autocomplete results for various settings pages # Autocomplete results for various settings pages
def default_autocomplete def default_autocomplete
[ [
{ category: "Settings", label: "Profile settings", url: profile_path }, { category: "Settings", label: "User settings", url: profile_path },
{ category: "Settings", label: "SSH Keys", url: profile_keys_path }, { category: "Settings", label: "SSH Keys", url: profile_keys_path },
{ category: "Settings", label: "Dashboard", url: root_path }, { category: "Settings", label: "Dashboard", url: root_path },
{ category: "Settings", label: "Admin Section", url: admin_root_path }, { category: "Settings", label: "Admin Section", url: admin_root_path },
...@@ -75,7 +75,7 @@ module SearchHelper ...@@ -75,7 +75,7 @@ module SearchHelper
{ category: "Current Project", label: "Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, { category: "Current Project", label: "Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) },
{ category: "Current Project", label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, { category: "Current Project", label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
{ category: "Current Project", label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, { category: "Current Project", label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
{ category: "Current Project", label: "Members", url: namespace_project_project_members_path(@project.namespace, @project) }, { category: "Current Project", label: "Members", url: namespace_project_settings_members_path(@project.namespace, @project) },
{ category: "Current Project", label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, { category: "Current Project", label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
] ]
else else
......
...@@ -36,8 +36,8 @@ module Milestoneish ...@@ -36,8 +36,8 @@ module Milestoneish
def issues_visible_to_user(user) def issues_visible_to_user(user)
memoize_per_user(user, :issues_visible_to_user) do memoize_per_user(user, :issues_visible_to_user) do
params = try(:project_id) ? { project_id: project_id } : {} IssuesFinder.new(user, issues_finder_params)
IssuesFinder.new(user, params).execute.where(milestone_id: milestoneish_ids) .execute.where(milestone_id: milestoneish_ids)
end end
end end
...@@ -72,4 +72,10 @@ module Milestoneish ...@@ -72,4 +72,10 @@ module Milestoneish
@memoized[method_name] ||= {} @memoized[method_name] ||= {}
@memoized[method_name][user.try!(:id)] ||= yield @memoized[method_name][user.try!(:id)] ||= yield
end end
# override in a class that includes this module to get a faster query
# from IssuesFinder
def issues_finder_params
{}
end
end end
class GlobalMilestone class GlobalMilestone
include Milestoneish include Milestoneish
EPOCH = DateTime.parse('1970-01-01')
attr_accessor :title, :milestones attr_accessor :title, :milestones
alias_attribute :name, :title alias_attribute :name, :title
...@@ -8,13 +10,22 @@ class GlobalMilestone ...@@ -8,13 +10,22 @@ class GlobalMilestone
@first_milestone @first_milestone
end end
def self.build_collection(milestones) def self.build_collection(projects, params)
milestones = milestones.group_by(&:title) child_milestones = MilestonesFinder.new.execute(projects, params)
milestones.map do |title, milestones| milestones = child_milestones.select(:id, :title).group_by(&:title).map do |title, grouped|
milestones_relation = Milestone.where(id: milestones.map(&:id)) milestones_relation = Milestone.where(id: grouped.map(&:id))
new(title, milestones_relation) new(title, milestones_relation)
end end
milestones.sort_by { |milestone| milestone.due_date || EPOCH }
end
def self.build(projects, title)
child_milestones = Milestone.of_projects(projects).where(title: title)
return if child_milestones.blank?
new(title, child_milestones)
end end
def initialize(title, milestones) def initialize(title, milestones)
......
class GroupMilestone < GlobalMilestone
attr_accessor :group
def self.build_collection(group, projects, params)
super(projects, params).each do |milestone|
milestone.group = group
end
end
def self.build(group, projects, title)
super(projects, title).tap do |milestone|
milestone.group = group if milestone
end
end
def issues_finder_params
{ group_id: group.id }
end
end
...@@ -49,6 +49,10 @@ class Key < ActiveRecord::Base ...@@ -49,6 +49,10 @@ class Key < ActiveRecord::Base
"key-#{id}" "key-#{id}"
end end
def update_last_used_at
UseKeyWorker.perform_async(self.id)
end
def add_to_shell def add_to_shell
GitlabShellWorker.perform_async( GitlabShellWorker.perform_async(
:add_key, :add_key,
......
...@@ -221,7 +221,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -221,7 +221,7 @@ class MergeRequest < ActiveRecord::Base
# true base commit, so we can't simply have `#diff_base_commit` fall back on # true base commit, so we can't simply have `#diff_base_commit` fall back on
# this method. # this method.
def likely_diff_base_commit def likely_diff_base_commit
first_commit.parent || first_commit first_commit.try(:parent) || first_commit
end end
def diff_start_commit def diff_start_commit
......
...@@ -202,4 +202,8 @@ class Milestone < ActiveRecord::Base ...@@ -202,4 +202,8 @@ class Milestone < ActiveRecord::Base
errors.add(:start_date, "Can't be greater than due date") errors.add(:start_date, "Can't be greater than due date")
end end
end end
def issues_finder_params
{ project_id: project_id }
end
end end
...@@ -37,6 +37,10 @@ class NotificationSetting < ActiveRecord::Base ...@@ -37,6 +37,10 @@ class NotificationSetting < ActiveRecord::Base
:success_pipeline :success_pipeline
] ]
EXCLUDED_WATCHER_EVENTS = [
:success_pipeline
]
store :events, accessors: EMAIL_EVENTS, coder: JSON store :events, accessors: EMAIL_EVENTS, coder: JSON
before_create :set_events before_create :set_events
......
...@@ -591,7 +591,10 @@ class NotificationService ...@@ -591,7 +591,10 @@ class NotificationService
custom_action = build_custom_key(action, target) custom_action = build_custom_key(action, target)
recipients = target.participants(current_user) recipients = target.participants(current_user)
recipients = add_project_watchers(recipients, project)
unless NotificationSetting::EXCLUDED_WATCHER_EVENTS.include?(custom_action)
recipients = add_project_watchers(recipients, project)
end
recipients = add_custom_notifications(recipients, project, custom_action) recipients = add_custom_notifications(recipients, project, custom_action)
recipients = reject_mention_users(recipients, project) recipients = reject_mention_users(recipients, project)
......
...@@ -36,7 +36,7 @@ module Projects ...@@ -36,7 +36,7 @@ module Projects
def groups def groups
current_user.authorized_groups.sort_by(&:path).map do |group| current_user.authorized_groups.sort_by(&:path).map do |group|
count = group.users.count count = group.users.count
{ username: group.path, name: group.name, count: count, avatar_url: group.avatar.url } { username: group.path, name: group.name, count: count, avatar_url: group.avatar_url }
end end
end end
......
...@@ -15,7 +15,7 @@ class ProjectPathValidator < ActiveModel::EachValidator ...@@ -15,7 +15,7 @@ class ProjectPathValidator < ActiveModel::EachValidator
# 'tree' as project name and 'deploy_keys' as route. # 'tree' as project name and 'deploy_keys' as route.
# #
RESERVED = (NamespaceValidator::RESERVED - RESERVED = (NamespaceValidator::RESERVED -
%w[dashboard help ci admin search notes services] + %w[dashboard help ci admin search notes services assets profile public] +
%w[tree commits wikis new edit create update logs_tree %w[tree commits wikis new edit create update logs_tree
preview blob blame raw files create_dir find_file]).freeze preview blob blame raw files create_dir find_file]).freeze
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
%li %li
= link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username } = link_to "Profile", current_user, class: 'profile-link', aria: { label: "Profile" }, data: { user: current_user.username }
%li %li
= link_to "Profile Settings", profile_path, aria: { label: "Profile Settings" } = link_to "Settings", profile_path, aria: { label: "Settings" }
%li %li
= link_to "Help", help_path, aria: { label: "Help" } = link_to "Help", help_path, aria: { label: "Help" }
%li.divider %li.divider
......
- if project_nav_tab? :team - if project_nav_tab? :team
= nav_link(controller: [:project_members, :teams]) do = nav_link(controller: [:members, :teams]) do
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do = link_to namespace_project_settings_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
%span %span
Members Members
- if can_edit - if can_edit
- if @project.allowed_to_share_with_group?
= nav_link(controller: :group_links) do
= link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
%span
Groups
= nav_link(controller: :deploy_keys) do = nav_link(controller: :deploy_keys) do
= link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do = link_to namespace_project_deploy_keys_path(@project.namespace, @project), title: 'Deploy Keys' do
%span %span
......
- page_title "Profile Settings" - page_title "User Settings"
- header_title "Profile Settings", profile_path unless header_title - header_title "User Settings", profile_path unless header_title
- sidebar "dashboard" - sidebar "dashboard"
- nav "profile" - nav "profile"
......
...@@ -6,6 +6,9 @@ ...@@ -6,6 +6,9 @@
= key.title = key.title
.description .description
= key.fingerprint = key.fingerprint
.last-used-at
last used:
= key.last_used_at ? time_ago_with_tooltip(key.last_used_at) : 'n/a'
.pull-right .pull-right
%span.key-created-at %span.key-created-at
created #{time_ago_with_tooltip(key.created_at)} created #{time_ago_with_tooltip(key.created_at)}
......
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
%li %li
%span.light Created on: %span.light Created on:
%strong= @key.created_at.to_s(:medium) %strong= @key.created_at.to_s(:medium)
%li
%span.light Last used on:
%strong= @key.last_used_at.try(:to_s, :medium) || 'N/A'
.col-md-8 .col-md-8
%p %p
......
%board-sidebar{ "inline-template" => true, %board-sidebar{ "inline-template" => true,
":current-user" => "#{current_user ? current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) : {}}" } ":current-user" => "#{current_user ? current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) : {}}" }
%aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" } %transition{ name: "boards-sidebar-slide" }
.issuable-sidebar %aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" }
.block.issuable-sidebar-header .issuable-sidebar
%span.issuable-header-text.hide-collapsed.pull-left .block.issuable-sidebar-header
%strong %span.issuable-header-text.hide-collapsed.pull-left
{{ issue.title }} %strong
%br/ {{ issue.title }}
%span %br/
= precede "#" do %span
{{ issue.id }} = precede "#" do
%a.gutter-toggle.pull-right{ role: "button", {{ issue.id }}
href: "#", %a.gutter-toggle.pull-right{ role: "button",
"@click.prevent" => "closeSidebar", href: "#",
"aria-label" => "Toggle sidebar" } "@click.prevent" => "closeSidebar",
= custom_icon("icon_close", size: 15) "aria-label" => "Toggle sidebar" }
.js-issuable-update = custom_icon("icon_close", size: 15)
= render "projects/boards/components/sidebar/assignee" .js-issuable-update
= render "projects/boards/components/sidebar/milestone" = render "projects/boards/components/sidebar/assignee"
= render "projects/boards/components/sidebar/due_date" = render "projects/boards/components/sidebar/milestone"
= render "projects/boards/components/sidebar/labels" = render "projects/boards/components/sidebar/due_date"
= render "projects/boards/components/sidebar/notifications" = render "projects/boards/components/sidebar/labels"
= render "projects/boards/components/sidebar/notifications"
...@@ -20,10 +20,10 @@ ...@@ -20,10 +20,10 @@
.form-group .form-group
= label_tag :expires_at, 'Access expiration date', class: 'label-light' = label_tag :expires_at, 'Access expiration date', class: 'label-light'
.clearable-input .clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date' = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date-groups', placeholder: 'Select access expiration date', id: 'expires_at_groups'
%i.clear-icon.js-clear-input %i.clear-icon.js-clear-input
.help-block .help-block
On this date, all users in the group will automatically lose access to this project. On this date, all members in the group will automatically lose access to this project.
= submit_tag "Share", class: "btn btn-create" = submit_tag "Share", class: "btn btn-create"
.col-lg-9.col-lg-offset-3 .col-lg-9.col-lg-offset-3
%hr %hr
......
...@@ -50,56 +50,55 @@ ...@@ -50,56 +50,55 @@
.content-block.content-block-small .content-block.content-block-small
= render 'award_emoji/awards_block', awardable: @merge_request, inline: true = render 'award_emoji/awards_block', awardable: @merge_request, inline: true
- if @commits_count.nonzero? .merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') }
.merge-request-tabs-holder{ class: ("js-tabs-affix" unless ENV['RAILS_ENV'] == 'test') } %div{ class: container_class }
%div{ class: container_class } %ul.merge-request-tabs.nav-links.no-top.no-bottom
%ul.merge-request-tabs.nav-links.no-top.no-bottom %li.notes-tab
%li.notes-tab = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#notes', action: 'notes', toggle: 'tab' } do Discussion
Discussion %span.badge= @merge_request.related_notes.user.count
%span.badge= @merge_request.related_notes.user.count - if @merge_request.source_project
- if @merge_request.source_project %li.commits-tab
%li.commits-tab = link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#commits', action: 'commits', toggle: 'tab' } do Commits
Commits %span.badge= @commits_count
%span.badge= @commits_count - if @pipelines.any?
- if @pipelines.any? %li.pipelines-tab
%li.pipelines-tab = link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do
= link_to pipelines_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: '#pipelines', action: 'pipelines', toggle: 'tab' } do Pipelines
Pipelines %span.badge= @pipelines.size
%span.badge= @pipelines.size %li.diffs-tab
%li.diffs-tab = link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do
= link_to diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: { target: 'div#diffs', action: 'diffs', toggle: 'tab' } do Changes
Changes %span.badge= @merge_request.diff_size
%span.badge= @merge_request.diff_size %li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true }
%li#resolve-count-app.line-resolve-all-container.pull-right.prepend-top-10.hidden-xs{ "v-cloak" => true } %resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" }
%resolve-count{ "inline-template" => true, ":logged-out" => "#{current_user.nil?}" } %div
%div .line-resolve-all{ "v-show" => "discussionCount > 0",
.line-resolve-all{ "v-show" => "discussionCount > 0", ":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" }
":class" => "{ 'has-next-btn': !loggedOut && resolvedDiscussionCount !== discussionCount }" } %span.line-resolve-btn.is-disabled{ type: "button",
%span.line-resolve-btn.is-disabled{ type: "button", ":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" }
":class" => "{ 'is-active': resolvedDiscussionCount === discussionCount }" } = render "shared/icons/icon_status_success.svg"
= render "shared/icons/icon_status_success.svg" %span.line-resolve-text
%span.line-resolve-text {{ resolvedDiscussionCount }}/{{ discussionCount }} {{ resolvedCountText }} resolved
{{ resolvedDiscussionCount }}/{{ discussionCount }} {{ resolvedCountText }} resolved = render "discussions/jump_to_next"
= render "discussions/jump_to_next"
.tab-content#diff-notes-app .tab-content#diff-notes-app
#notes.notes.tab-pane.voting_notes #notes.notes.tab-pane.voting_notes
.row .row
%section.col-md-12 %section.col-md-12
.issuable-discussion .issuable-discussion
= render "projects/merge_requests/discussion" = render "projects/merge_requests/discussion"
#commits.commits.tab-pane #commits.commits.tab-pane
- # This tab is always loaded via AJAX - # This tab is always loaded via AJAX
#pipelines.pipelines.tab-pane #pipelines.pipelines.tab-pane
- # This tab is always loaded via AJAX - # This tab is always loaded via AJAX
#diffs.diffs.tab-pane #diffs.diffs.tab-pane
- # This tab is always loaded via AJAX - # This tab is always loaded via AJAX
.mr-loading-status .mr-loading-status
= spinner = spinner
= render 'shared/issuable/sidebar', issuable: @merge_request = render 'shared/issuable/sidebar', issuable: @merge_request
- if @merge_request.can_be_reverted?(current_user) - if @merge_request.can_be_reverted?(current_user)
......
%ol#commits-list.list-unstyled - if @commits.empty?
= render "projects/commits/commits", project: @merge_request.source_project, ref: @merge_request.source_branch .commits-empty
%h4
There are no commits yet.
= custom_icon ('illustration_no_commits')
- else
%ol#commits-list.list-unstyled
= render "projects/commits/commits", project: @merge_request.source_project, ref: @merge_request.source_branch
.note-edit-form .note-edit-form
= form_tag '#', method: :put, remote: true, class: 'edit-note common-note-form js-quick-submit' do = form_tag '#', method: :put, class: 'edit-note common-note-form js-quick-submit' do
= hidden_field_tag :authenticity_token, form_authenticity_token
= hidden_field_tag :target_id, '', class: 'js-form-target-id' = hidden_field_tag :target_id, '', class: 'js-form-target-id'
= hidden_field_tag :target_type, '', class: 'js-form-target-type' = hidden_field_tag :target_type, '', class: 'js-form-target-type'
= render layout: 'projects/md_preview', locals: { preview_class: 'md-preview', referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: 'md-preview', referenced_users: true } do
......
...@@ -23,4 +23,4 @@ ...@@ -23,4 +23,4 @@
to post a comment to post a comment
:javascript :javascript
var notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}") var notes = new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, project_id: @project, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}")
.row.prepend-top-default
.col-lg-3.settings-sidebar
%h4.prepend-top-0
Members
- if can?(current_user, :admin_project_member, @project)
%p
Add a new member to
%strong= @project.name
.col-lg-9
.light.prepend-top-default
- if can?(current_user, :admin_project_member, @project)
= render "projects/project_members/new_project_member"
= render 'shared/members/requests', membership_source: @project, requesters: @requesters
.append-bottom-default.clearfix
%h5.member.existing-title
Existing members and groups
- if @group_links.any?
= render 'projects/project_members/groups', group_links: @group_links
= render 'projects/project_members/team', members: @project_members
= paginate @project_members, theme: "gitlab"
= form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'users-project-form' } do |f| = form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'users-project-form' } do |f|
.row .form-group
.col-md-4.col-lg-6 = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
= users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true) .help-block.append-bottom-10
.help-block.append-bottom-10 Search for members by name, username, or email, or invite new ones using their email address.
Search for users by name, username, or email, or invite new ones using their email address. .form-group
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select"
.col-md-3.col-lg-2 .help-block.append-bottom-10
= select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select" = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
.help-block.append-bottom-10 about role permissions
= link_to "Read more", help_page_path("user/permissions"), class: "vlink" .form-group
about role permissions .clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date'
.col-md-3.col-lg-2 %i.clear-icon.js-clear-input
.clearable-input .help-block.append-bottom-10
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date' On this date, the member(s) will automatically lose access to this project.
%i.clear-icon.js-clear-input = f.submit "Add to project", class: "btn btn-create"
.help-block.append-bottom-10 = link_to "Import", import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-default", title: "Import members from another project"
On this date, the user(s) will automatically lose access to this project.
.col-md-2
= f.submit "Add to project", class: "btn btn-create btn-block"
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
Users with access to Members with access to
%strong #{@project.name} %strong #{@project.name}
%span.badge= @project_members.total_count %span.badge= @project_members.total_count
= form_tag namespace_project_settings_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
= render 'shared/members/sort_dropdown'
%ul.content-list %ul.content-list
= render partial: 'shared/members/member', collection: members, as: :member = render partial: 'shared/members/member', collection: members, as: :member
...@@ -12,5 +12,4 @@ ...@@ -12,5 +12,4 @@
.form-actions .form-actions
= button_tag 'Import project members', class: "btn btn-create" = button_tag 'Import project members', class: "btn btn-create"
= link_to "Cancel", namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-cancel" = link_to "Cancel", namespace_project_settings_members_path(@project.namespace, @project), class: "btn btn-cancel"
- page_title "Members"
.project-members-page.prepend-top-default
%h4.project-members-title.clearfix
Members
= link_to "Import", import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-default pull-right hidden-xs", title: "Import members from another project"
- if can?(current_user, :admin_project_member, @project)
.project-members-new.append-bottom-default
%p.clearfix
Add new user to
%strong= @project.name
= render "new_project_member"
= render 'shared/members/requests', membership_source: @project, requesters: @requesters
.append-bottom-default.clearfix
%h5.member.existing-title
Existing users and groups
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
= search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
%button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
= icon("search")
= render 'shared/members/sort_dropdown'
- if @group_links.any?
= render 'groups', group_links: @group_links
= render 'team', members: @project_members
= paginate @project_members, theme: "gitlab"
- page_title "Members"
= render "projects/project_members/index"
- if can?(current_user, :admin_project, @project)
- if @project.allowed_to_share_with_group?
= render "projects/group_links/index"
...@@ -97,7 +97,7 @@ ...@@ -97,7 +97,7 @@
remove due date remove due date
- if can?(current_user, :"admin_#{issuable.to_ability_name}", @project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.selectbox.hide-collapsed .selectbox.hide-collapsed
= f.hidden_field :due_date, value: issuable.due_date = f.hidden_field :due_date, value: issuable.due_date.try(:strftime, 'yy-mm-dd')
.dropdown .dropdown
%button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } } %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } }
%span.dropdown-toggle-text Due date %span.dropdown-toggle-text Due date
......
...@@ -37,7 +37,6 @@ ...@@ -37,7 +37,6 @@
%i.clear-icon.js-clear-input %i.clear-icon.js-clear-input
- if can_admin_member - if can_admin_member
= link_to namespace_project_group_link_path(@project.namespace, @project, group_link), = link_to namespace_project_group_link_path(@project.namespace, @project, group_link),
remote: true,
method: :delete, method: :delete,
data: { confirm: "Are you sure you want to remove #{group.name}?" }, data: { confirm: "Are you sure you want to remove #{group.name}?" },
class: 'btn btn-remove prepend-left-10' do class: 'btn btn-remove prepend-left-10' do
......
class UseKeyWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
def perform(key_id)
key = Key.find(key_id)
key.touch(:last_used_at)
rescue ActiveRecord::RecordNotFound
Rails.logger.error("UseKeyWorker: couldn't find key with ID=#{key_id}, skipping job")
false
end
end
---
title: Fix double spaced CI log
merge_request: 8349
author: Jared Deckard <jared.deckard@gmail.com>
---
title: fix border in login session tabs
merge_request: 8346
author:
---
title: Adds label to Environments "Date Created"
merge_request: 8376
author: Saad Shahd
---
title: Combined the settings options project members and groups into a single one
called members
merge_request:
author:
---
title: Re-order update steps in the 8.14 -> 8.15 upgrade guide
merge_request:
author:
---
title: Don't instrument 405 Grape calls
merge_request: 8445
author:
---
title: Cache project authorizations even when user has access to zero projects
merge_request: 8327
author:
---
title: display merge request discussion tab for empty branches
merge_request: 8347
author:
---
title: Increases width of mini-pipeline-graph dropdown to prevent wrong position on chrome on ubuntu
merge_request: 8399
author:
---
title: Precompile all JavaScript fixtures
merge_request: 8384
author:
---
title: Removes invalid html and unneed CSS to prevent shaking in the pipelines tab
merge_request: 8411
author:
---
title: 26352 Change Profile settings to User / Settings
merge_request:
author:
---
title: Rename wiki_events to wiki_page_events in project hooks API to avoid errors
merge_request: Robert Schilling
author: 8425
---
title: Fix 500 error when visit group from admin area if group name contains dot
merge_request:
author:
---
title: Rename projects wth reserved names
merge_request: 8234
author:
---
title: Log LDAP blocking/unblocking events to application log
merge_request: 8042
author: Markus Koller
---
title: Fix broken url on group avatar
merge_request: 8464
author: hogewest
---
title: Fix cross-project references copy to include the project reference
merge_request:
author:
---
title: Fix 500 error renaming group
merge_request:
author:
---
title: Fix a minor grammar error in merge request widget
merge_request: 8337
author:
---
title: Fix date inconsistency on due date picker
merge_request: 7422
author: Giuliano Varriale
---
title: Record and show last used date of SSH Keys
merge_request: 8113
author: Vincent Wong
--- ---
title: Fixed GFM dropdown not showing on new lines title: Added animations to issue boards interactions
merge_request: merge_request:
author: author:
---
title: Gitlab::LDAP::Person uses LDAP attributes configuration
merge_request: 8418
author:
---
title: Fix unclear closing issue behaviour on Merge Request show page
merge_request: 8345
author: Gabriel Gizotti
---
title: Copy, don't move uploaded avatar files
merge_request: 8396
author:
---
title: Fixed regression of note-headline-light where it was always placed on 2 lines, even on wide viewports
merge_request:
author:
---
title: Make successful pipeline emails off for watchers
merge_request: 8176
author:
---
title: Speed up group milestone index by passing group_id to IssuesFinder
merge_request: 8363
author:
---
title: Re-add Google Cloud Storage as a backup strategy
merge_request:
author:
...@@ -307,6 +307,10 @@ constraints(ProjectUrlConstrainer.new) do ...@@ -307,6 +307,10 @@ constraints(ProjectUrlConstrainer.new) do
end end
end end
namespace :settings do
resource :members, only: [:show]
end
# Since both wiki and repository routing contains wildcard characters # Since both wiki and repository routing contains wildcard characters
# its preferable to keep it below all other project routes # its preferable to keep it below all other project routes
draw :wiki draw :wiki
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
- [email_receiver, 2] - [email_receiver, 2]
- [emails_on_push, 2] - [emails_on_push, 2]
- [mailers, 2] - [mailers, 2]
- [use_key, 1]
- [repository_fork, 1] - [repository_fork, 1]
- [repository_import, 1] - [repository_import, 1]
- [project_service, 1] - [project_service, 1]
......
class AddLastUsedAtToKey < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :keys, :last_used_at, :datetime
end
end
...@@ -10,7 +10,6 @@ class RenameReservedProjectNames < ActiveRecord::Migration ...@@ -10,7 +10,6 @@ class RenameReservedProjectNames < ActiveRecord::Migration
KNOWN_PATHS = %w(.well-known KNOWN_PATHS = %w(.well-known
all all
assets
blame blame
blob blob
commits commits
...@@ -18,7 +17,6 @@ class RenameReservedProjectNames < ActiveRecord::Migration ...@@ -18,7 +17,6 @@ class RenameReservedProjectNames < ActiveRecord::Migration
create_dir create_dir
edit edit
files files
files
find_file find_file
groups groups
hooks hooks
...@@ -26,11 +24,8 @@ class RenameReservedProjectNames < ActiveRecord::Migration ...@@ -26,11 +24,8 @@ class RenameReservedProjectNames < ActiveRecord::Migration
logs_tree logs_tree
merge_requests merge_requests
new new
new
preview preview
profile
projects projects
public
raw raw
repository repository
robots.txt robots.txt
......
...@@ -528,6 +528,7 @@ ActiveRecord::Schema.define(version: 20161227192806) do ...@@ -528,6 +528,7 @@ ActiveRecord::Schema.define(version: 20161227192806) do
t.string "fingerprint" t.string "fingerprint"
t.boolean "public", default: false, null: false t.boolean "public", default: false, null: false
t.boolean "can_push", default: false, null: false t.boolean "can_push", default: false, null: false
t.datetime "last_used_at"
end end
add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree add_index "keys", ["fingerprint"], name: "index_keys_on_fingerprint", unique: true, using: :btree
......
...@@ -298,8 +298,11 @@ LDAP server please double-check the LDAP `port` and `method` settings used by ...@@ -298,8 +298,11 @@ LDAP server please double-check the LDAP `port` and `method` settings used by
GitLab. Common combinations are `method: 'plain'` and `port: 389`, OR GitLab. Common combinations are `method: 'plain'` and `port: 389`, OR
`method: 'ssl'` and `port: 636`. `method: 'ssl'` and `port: 636`.
### Login with valid credentials rejected ### Troubleshooting
If there is an unexpected error while authenticating the user with the LDAP If a user account is blocked or unblocked due to the LDAP configuration, a
backend, the login is rejected and details about the error are logged to message will be logged to `application.log`.
If there is an unexpected error during an LDAP lookup (configuration error,
timeout), the login is rejected and a message will be logged to
`production.log`. `production.log`.
...@@ -71,8 +71,8 @@ If you want to use Gmail / Google Apps with Reply by email, make sure you have ...@@ -71,8 +71,8 @@ If you want to use Gmail / Google Apps with Reply by email, make sure you have
[IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018) [IMAP access enabled](https://support.google.com/mail/troubleshooter/1668960?hl=en#ts=1665018)
and [allowed less secure apps to access the account](https://support.google.com/accounts/answer/6010255). and [allowed less secure apps to access the account](https://support.google.com/accounts/answer/6010255).
To set up a basic Postfix mail server with IMAP access on Ubuntu, follow To set up a basic Postfix mail server with IMAP access on Ubuntu, follow the
[these instructions](./postfix.md). [Postfix setup documentation](reply_by_email_postfix_setup.md).
### Omnibus package installations ### Omnibus package installations
......
This diff is collapsed.
...@@ -271,9 +271,9 @@ sudo usermod -aG redis git ...@@ -271,9 +271,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 8-15-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-16-stable gitlab
**Note:** You can change `8-15-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! **Note:** You can change `8-16-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It ### Configure It
...@@ -400,16 +400,10 @@ GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). The ...@@ -400,16 +400,10 @@ GitLab-Workhorse uses [GNU Make](https://www.gnu.org/software/make/). The
following command-line will install GitLab-Workhorse in `/home/git/gitlab-workhorse` following command-line will install GitLab-Workhorse in `/home/git/gitlab-workhorse`
which is the recommended location. which is the recommended location.
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
### Initialize Database and Activate Advanced Features ### Initialize Database and Activate Advanced Features
# Go to GitLab installation folder
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
# Type 'yes' to create the database tables. # Type 'yes' to create the database tables.
......
...@@ -88,7 +88,7 @@ It uses the [Fog library](http://fog.io/) to perform the upload. ...@@ -88,7 +88,7 @@ It uses the [Fog library](http://fog.io/) to perform the upload.
In the example below we use Amazon S3 for storage, but Fog also lets you use In the example below we use Amazon S3 for storage, but Fog also lets you use
[other storage providers](http://fog.io/storage/). GitLab [other storage providers](http://fog.io/storage/). GitLab
[imports cloud drivers](https://gitlab.com/gitlab-org/gitlab-ce/blob/30f5b9a5b711b46f1065baf755e413ceced5646b/Gemfile#L88) [imports cloud drivers](https://gitlab.com/gitlab-org/gitlab-ce/blob/30f5b9a5b711b46f1065baf755e413ceced5646b/Gemfile#L88)
for AWS, OpenStack Swift and Rackspace as well. A local driver is for AWS, Google, OpenStack Swift and Rackspace as well. A local driver is
[also available](#uploading-to-locally-mounted-shares). [also available](#uploading-to-locally-mounted-shares).
For omnibus packages: For omnibus packages:
......
...@@ -11,12 +11,15 @@ guide links by version. ...@@ -11,12 +11,15 @@ guide links by version.
### 1. Stop server ### 1. Stop server
sudo service gitlab stop ```bash
sudo service gitlab stop
```
### 2. Backup ### 2. Backup
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
``` ```
...@@ -49,6 +52,8 @@ sudo gem install bundler --no-ri --no-rdoc ...@@ -49,6 +52,8 @@ sudo gem install bundler --no-ri --no-rdoc
### 4. Get latest code ### 4. Get latest code
```bash ```bash
cd /home/git/gitlab
sudo -u git -H git fetch --all sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
``` ```
...@@ -56,6 +61,8 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut ...@@ -56,6 +61,8 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut
For GitLab Community Edition: For GitLab Community Edition:
```bash ```bash
cd /home/git/gitlab
sudo -u git -H git checkout 8-15-stable sudo -u git -H git checkout 8-15-stable
``` ```
...@@ -64,28 +71,12 @@ OR ...@@ -64,28 +71,12 @@ OR
For GitLab Enterprise Edition: For GitLab Enterprise Edition:
```bash ```bash
sudo -u git -H git checkout 8-15-stable-ee cd /home/git/gitlab
```
### 5. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v4.1.1
```
### 6. Update gitlab-workhorse
Install and compile gitlab-workhorse. This requires
[Go 1.5](https://golang.org/dl) which should already be on your system from
GitLab 8.1.
```bash sudo -u git -H git checkout 8-15-stable-ee
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
``` ```
### 7. Install libs, migrations, etc. ### 5. Install libs, migrations, etc.
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
...@@ -106,6 +97,27 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production ...@@ -106,6 +97,27 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
``` ```
### 6. Update gitlab-workhorse
Install and compile gitlab-workhorse. This requires
[Go 1.5](https://golang.org/dl) which should already be on your system from
GitLab 8.1.
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
```
### 7. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v4.1.1
```
### 8. Update configuration files ### 8. Update configuration files
#### New configuration options for `gitlab.yml` #### New configuration options for `gitlab.yml`
...@@ -113,6 +125,8 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS ...@@ -113,6 +125,8 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS
There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`: There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
```sh ```sh
cd /home/git/gitlab
git diff origin/8-14-stable:config/gitlab.yml.example origin/8-15-stable:config/gitlab.yml.example git diff origin/8-14-stable:config/gitlab.yml.example origin/8-15-stable:config/gitlab.yml.example
``` ```
...@@ -122,6 +136,8 @@ Configure Git to generate packfile bitmaps (introduced in Git 2.0) on ...@@ -122,6 +136,8 @@ Configure Git to generate packfile bitmaps (introduced in Git 2.0) on
the GitLab server during `git gc`. the GitLab server during `git gc`.
```sh ```sh
cd /home/git/gitlab
sudo -u git -H git config --global repack.writeBitmaps true sudo -u git -H git config --global repack.writeBitmaps true
``` ```
...@@ -130,6 +146,8 @@ sudo -u git -H git config --global repack.writeBitmaps true ...@@ -130,6 +146,8 @@ sudo -u git -H git config --global repack.writeBitmaps true
Ensure you're still up-to-date with the latest NGINX configuration changes: Ensure you're still up-to-date with the latest NGINX configuration changes:
```sh ```sh
cd /home/git/gitlab
# For HTTPS configurations # For HTTPS configurations
git diff origin/8-14-stable:lib/support/nginx/gitlab-ssl origin/8-15-stable:lib/support/nginx/gitlab-ssl git diff origin/8-14-stable:lib/support/nginx/gitlab-ssl origin/8-15-stable:lib/support/nginx/gitlab-ssl
...@@ -162,26 +180,42 @@ See [smtp_settings.rb.sample] as an example. ...@@ -162,26 +180,42 @@ See [smtp_settings.rb.sample] as an example.
Ensure you're still up-to-date with the latest init script changes: Ensure you're still up-to-date with the latest init script changes:
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ```bash
cd /home/git/gitlab
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
```
For Ubuntu 16.04.1 LTS: For Ubuntu 16.04.1 LTS:
sudo systemctl daemon-reload ```bash
sudo systemctl daemon-reload
```
### 9. Start application ### 9. Start application
sudo service gitlab start ```bash
sudo service nginx restart sudo service gitlab start
sudo service nginx restart
```
### 10. Check application status ### 10. Check application status
Check if GitLab and its environment are configured correctly: Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production ```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
```
To make sure you didn't miss anything run a more thorough check: To make sure you didn't miss anything run a more thorough check:
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production ```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
If all items are green, then congratulations, the upgrade is complete! If all items are green, then congratulations, the upgrade is complete!
...@@ -196,6 +230,7 @@ database migration (the backup is already migrated to the previous version). ...@@ -196,6 +230,7 @@ database migration (the backup is already migrated to the previous version).
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
``` ```
......
# From 8.15 to 8.16
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
top left corner of GitLab (below the menu bar).
If the highest number stable branch is unclear please check the
[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
guide links by version.
### 1. Stop server
```bash
sudo service gitlab stop
```
### 2. Backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
```
### 3. Update Ruby
We will continue supporting Ruby < 2.3 for the time being but we recommend you
upgrade to Ruby 2.3 if you're running a source installation, as this is the same
version that ships with our Omnibus package.
You can check which version you are running with `ruby -v`.
Download and compile Ruby:
```bash
mkdir /tmp/ruby && cd /tmp/ruby
curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.gz
echo 'a8db9ce7f9110320f33b8325200e3ecfbd2b534b ruby-2.3.3.tar.gz' | shasum -c - && tar xzf ruby-2.3.3.tar.gz
cd ruby-2.3.3
./configure --disable-install-rdoc
make
sudo make install
```
Install Bundler:
```bash
sudo gem install bundler --no-ri --no-rdoc
```
### 4. Get latest code
```bash
cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
```
For GitLab Community Edition:
```bash
cd /home/git/gitlab
sudo -u git -H git checkout 8-16-stable
```
OR
For GitLab Enterprise Edition:
```bash
cd /home/git/gitlab
sudo -u git -H git checkout 8-16-stable-ee
```
### 5. Install libs, migrations, etc.
```bash
cd /home/git/gitlab
# MySQL installations (note: the line below states '--without postgres')
sudo -u git -H bundle install --without postgres development test --deployment
# PostgreSQL installations (note: the line below states '--without mysql')
sudo -u git -H bundle install --without mysql development test --deployment
# Optional: clean up old gems
sudo -u git -H bundle clean
# Run database migrations
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
```
### 6. Update gitlab-workhorse
Install and compile gitlab-workhorse. This requires
[Go 1.5](https://golang.org/dl) which should already be on your system from
GitLab 8.1.
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
```
### 7. Update gitlab-shell
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v4.1.1
```
### 8. Update configuration files
#### New configuration options for `gitlab.yml`
There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
```sh
cd /home/git/gitlab
git diff origin/8-15-stable:config/gitlab.yml.example origin/8-16-stable:config/gitlab.yml.example
```
#### Git configuration
Configure Git to generate packfile bitmaps (introduced in Git 2.0) on
the GitLab server during `git gc`.
```sh
cd /home/git/gitlab
sudo -u git -H git config --global repack.writeBitmaps true
```
#### Nginx configuration
Ensure you're still up-to-date with the latest NGINX configuration changes:
```sh
cd /home/git/gitlab
# For HTTPS configurations
git diff origin/8-15-stable:lib/support/nginx/gitlab-ssl origin/8-16-stable:lib/support/nginx/gitlab-ssl
# For HTTP configurations
git diff origin/8-15-stable:lib/support/nginx/gitlab origin/8-16-stable:lib/support/nginx/gitlab
```
If you are using Apache instead of NGINX please see the updated [Apache templates].
Also note that because Apache does not support upstreams behind Unix sockets you
will need to let gitlab-workhorse listen on a TCP port. You can do this
via [/etc/default/gitlab].
[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-16-stable/lib/support/init.d/gitlab.default.example#L38
#### SMTP configuration
If you're installing from source and use SMTP to deliver mail, you will need to add the following line
to config/initializers/smtp_settings.rb:
```ruby
ActionMailer::Base.delivery_method = :smtp
```
See [smtp_settings.rb.sample] as an example.
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-16-stable/config/initializers/smtp_settings.rb.sample#L13
#### Init script
Ensure you're still up-to-date with the latest init script changes:
```bash
cd /home/git/gitlab
sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
```
For Ubuntu 16.04.1 LTS:
```bash
sudo systemctl daemon-reload
```
### 9. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
### 10. Check application status
Check if GitLab and its environment are configured correctly:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
```
To make sure you didn't miss anything run a more thorough check:
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
```
If all items are green, then congratulations, the upgrade is complete!
## Things went south? Revert to previous version (8.15)
### 1. Revert the code to the previous version
Follow the [upgrade guide from 8.14 to 8.15](8.14-to-8.15.md), except for the
database migration (the backup is already migrated to the previous version).
### 2. Restore from the backup
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
...@@ -14,6 +14,7 @@ user on the database version) ...@@ -14,6 +14,7 @@ user on the database version)
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
``` ```
...@@ -32,28 +33,13 @@ current version with `cat VERSION`). ...@@ -32,28 +33,13 @@ current version with `cat VERSION`).
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
sudo -u git -H git fetch --all sudo -u git -H git fetch --all
sudo -u git -H git checkout -- Gemfile.lock db/schema.rb sudo -u git -H git checkout -- Gemfile.lock db/schema.rb
sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG
``` ```
### 3. Update gitlab-shell to the corresponding version ### 3. Install libs, migrations, etc.
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch
sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`
```
### 4. Update gitlab-workhorse to the corresponding version
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
```
### 5. Install libs, migrations, etc.
```bash ```bash
cd /home/git/gitlab cd /home/git/gitlab
...@@ -74,6 +60,23 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production ...@@ -74,6 +60,23 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
``` ```
### 4. Update gitlab-workhorse to the corresponding version
```bash
cd /home/git/gitlab
sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
```
### 5. Update gitlab-shell to the corresponding version
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`
```
### 6. Start application ### 6. Start application
```bash ```bash
...@@ -86,6 +89,8 @@ sudo service nginx restart ...@@ -86,6 +89,8 @@ sudo service nginx restart
Check if GitLab and its environment are configured correctly: Check if GitLab and its environment are configured correctly:
```bash ```bash
cd /home/git/gitlab
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
``` ```
......
...@@ -5,8 +5,7 @@ Import your projects from Gitea to GitLab with minimal effort. ...@@ -5,8 +5,7 @@ Import your projects from Gitea to GitLab with minimal effort.
## Overview ## Overview
>**Note:** >**Note:**
As of Gitea `v1.0.0`, issue & pull-request comments cannot be imported! This is This requires Gitea `v1.0.0` or newer.
a [known issue][issue-401] that should be fixed in a near-future.
- At its current state, Gitea importer can import: - At its current state, Gitea importer can import:
- the repository description (GitLab 8.15+) - the repository description (GitLab 8.15+)
...@@ -76,5 +75,3 @@ If you want, you can import all your Gitea projects in one go by hitting ...@@ -76,5 +75,3 @@ If you want, you can import all your Gitea projects in one go by hitting
You can also choose a different name for the project and a different namespace, You can also choose a different name for the project and a different namespace,
if you have the privileges to do so. if you have the privileges to do so.
[issue-401]: https://github.com/go-gitea/gitea/issues/401
...@@ -73,7 +73,7 @@ In all of the below cases, the notification will be sent to: ...@@ -73,7 +73,7 @@ In all of the below cases, the notification will be sent to:
...with notification level "Participating" or higher ...with notification level "Participating" or higher
- Watchers: users with notification level "Watch" - Watchers: users with notification level "Watch" (however successful pipeline would be off for watchers)
- Subscribers: anyone who manually subscribed to the issue/merge request - Subscribers: anyone who manually subscribed to the issue/merge request
- Custom: Users with notification level "custom" who turned on notifications for any of the events present in the table below - Custom: Users with notification level "custom" who turned on notifications for any of the events present in the table below
......
...@@ -113,8 +113,10 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps ...@@ -113,8 +113,10 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
project.team << [user, :reporter] project.team << [user, :reporter]
end end
step 'I click link "Import team from another project"' do step 'I click link "Import team from another project"' do
click_link "Import" page.within '.users-project-form' do
click_link "Import"
end
end end
When 'I submit "Website" project for import team' do When 'I submit "Website" project for import team' do
......
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