Commit 3744d629 authored by Lin Jen-Shin's avatar Lin Jen-Shin

Merge remote-tracking branch 'upstream/master' into pipeline-notifications

* upstream/master: (70 commits)
  Fix routing spec for group controller
  Add small improvements to constrainers and specs
  Faster search
  Fix broken commits search
  Changed helper method to check for none on params Moved if statements around in view
  API: Return 400 when creating a systemhook fails
  Update non-exist group spinach test to match routing
  Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2
  Replace trigger with the new ID of the docs project
  Refactor method name
  17492 Update link color for more accessible contrast
  Fixed todos empty state when filtering
  Refactor namespace regex
  implements reset incoming email token on issues modal and account page, reactivates all tests and writes more tests for it
  Use separate email-friendly token for incoming email and let incoming email token be reset
  Use the Gitlab Workhorse HTTP header in the admin dashboard
  Refactor project routing
  Fix 404 when visit /projects page
  Rewritten spinach git_blame tests to rspec feature tests
  Add tests for project#index routing
  ...
parents d0361573 01083870
...@@ -331,7 +331,7 @@ trigger_docs: ...@@ -331,7 +331,7 @@ trigger_docs:
cache: {} cache: {}
artifacts: {} artifacts: {}
script: script:
- "curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=ce https://gitlab.com/api/v3/projects/38069/trigger/builds" - "curl -X POST -F token=${DOCS_TRIGGER_TOKEN} -F ref=master -F variables[PROJECT]=ce https://gitlab.com/api/v3/projects/1794617/trigger/builds"
only: only:
- master@gitlab-org/gitlab-ce - master@gitlab-org/gitlab-ce
......
...@@ -6,6 +6,7 @@ entry. ...@@ -6,6 +6,7 @@ entry.
- Show correct environment log in admin/logs (@duk3luk3 !7191) - Show correct environment log in admin/logs (@duk3luk3 !7191)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117 - Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Diff collapse won't shift when collapsing.
- Backups do not fail anymore when using tar on annex and custom_hooks only. !5814 - Backups do not fail anymore when using tar on annex and custom_hooks only. !5814
- Adds user project membership expired event to clarify why user was removed (Callum Dryden) - Adds user project membership expired event to clarify why user was removed (Callum Dryden)
- Trim leading and trailing whitespace on project_path (Linus Thiel) - Trim leading and trailing whitespace on project_path (Linus Thiel)
...@@ -13,6 +14,7 @@ entry. ...@@ -13,6 +14,7 @@ entry.
- Adds support for the `token` attribute in project hooks API (Gauvain Pocentek) - Adds support for the `token` attribute in project hooks API (Gauvain Pocentek)
- Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO) - Adds an optional path parameter to the Commits API to filter commits by path (Luis HGO)
- Fix Markdown styling inside reference links (Jan Zdráhal) - Fix Markdown styling inside reference links (Jan Zdráhal)
- Create new issue board list after creating a new label
- Fix extra space on Build sidebar on Firefox !7060 - Fix extra space on Build sidebar on Firefox !7060
- Fail gracefully when creating merge request with non-existing branch (alexsanford) - Fail gracefully when creating merge request with non-existing branch (alexsanford)
- Fix mobile layout issues in admin user overview page !7087 - Fix mobile layout issues in admin user overview page !7087
...@@ -66,8 +68,10 @@ entry. ...@@ -66,8 +68,10 @@ entry.
- In all filterable drop downs, put input field in focus only after load is complete (Ido @leibo) - In all filterable drop downs, put input field in focus only after load is complete (Ido @leibo)
- Improve search query parameter naming in /admin/users !7115 (YarNayar) - Improve search query parameter naming in /admin/users !7115 (YarNayar)
- Fix table pagination to be responsive - Fix table pagination to be responsive
- Fix applying GitHub-imported labels when importing job is interrupted
- Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar) - Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar)
- Updated commit SHA styling on the branches page. - Updated commit SHA styling on the branches page.
- Fix 404 when visit /projects page
## 8.13.3 (2016-11-02) ## 8.13.3 (2016-11-02)
......
...@@ -26,7 +26,7 @@ gem 'omniauth-bitbucket', '~> 0.0.2' ...@@ -26,7 +26,7 @@ gem 'omniauth-bitbucket', '~> 0.0.2'
gem 'omniauth-cas3', '~> 1.1.2' gem 'omniauth-cas3', '~> 1.1.2'
gem 'omniauth-facebook', '~> 4.0.0' gem 'omniauth-facebook', '~> 4.0.0'
gem 'omniauth-github', '~> 1.1.1' gem 'omniauth-github', '~> 1.1.1'
gem 'omniauth-gitlab', '~> 1.0.0' gem 'omniauth-gitlab', '~> 1.0.2'
gem 'omniauth-google-oauth2', '~> 0.4.1' gem 'omniauth-google-oauth2', '~> 0.4.1'
gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
gem 'omniauth-saml', '~> 1.7.0' gem 'omniauth-saml', '~> 1.7.0'
...@@ -152,7 +152,7 @@ gem 'settingslogic', '~> 2.0.9' ...@@ -152,7 +152,7 @@ gem 'settingslogic', '~> 2.0.9'
gem 'version_sorter', '~> 2.1.0' gem 'version_sorter', '~> 2.1.0'
# Cache # Cache
gem 'redis-rails', '~> 4.0.0' gem 'redis-rails', '~> 5.0.1'
# Redis # Redis
gem 'redis', '~> 3.2' gem 'redis', '~> 3.2'
......
...@@ -456,7 +456,7 @@ GEM ...@@ -456,7 +456,7 @@ GEM
omniauth-github (1.1.2) omniauth-github (1.1.2)
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-oauth2 (~> 1.1) omniauth-oauth2 (~> 1.1)
omniauth-gitlab (1.0.1) omniauth-gitlab (1.0.2)
omniauth (~> 1.0) omniauth (~> 1.0)
omniauth-oauth2 (~> 1.0) omniauth-oauth2 (~> 1.0)
omniauth-google-oauth2 (0.4.1) omniauth-google-oauth2 (0.4.1)
...@@ -573,23 +573,23 @@ GEM ...@@ -573,23 +573,23 @@ GEM
json json
redcarpet (3.3.3) redcarpet (3.3.3)
redis (3.2.2) redis (3.2.2)
redis-actionpack (4.0.1) redis-actionpack (5.0.1)
actionpack (~> 4) actionpack (>= 4.0, < 6)
redis-rack (~> 1.5.0) redis-rack (>= 1, < 3)
redis-store (~> 1.1.0) redis-store (>= 1.1.0, < 1.4.0)
redis-activesupport (4.1.5) redis-activesupport (5.0.1)
activesupport (>= 3, < 5) activesupport (>= 3, < 6)
redis-store (~> 1.1.0) redis-store (~> 1.2.0)
redis-namespace (1.5.2) redis-namespace (1.5.2)
redis (~> 3.0, >= 3.0.4) redis (~> 3.0, >= 3.0.4)
redis-rack (1.5.0) redis-rack (1.6.0)
rack (~> 1.5) rack (~> 1.5)
redis-store (~> 1.1.0) redis-store (~> 1.2.0)
redis-rails (4.0.0) redis-rails (5.0.1)
redis-actionpack (~> 4) redis-actionpack (~> 5.0.0)
redis-activesupport (~> 4) redis-activesupport (~> 5.0.0)
redis-store (~> 1.1.0) redis-store (~> 1.2.0)
redis-store (1.1.7) redis-store (1.2.0)
redis (>= 2.2) redis (>= 2.2)
request_store (1.3.1) request_store (1.3.1)
rerun (0.11.0) rerun (0.11.0)
...@@ -913,7 +913,7 @@ DEPENDENCIES ...@@ -913,7 +913,7 @@ DEPENDENCIES
omniauth-cas3 (~> 1.1.2) omniauth-cas3 (~> 1.1.2)
omniauth-facebook (~> 4.0.0) omniauth-facebook (~> 4.0.0)
omniauth-github (~> 1.1.1) omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0) omniauth-gitlab (~> 1.0.2)
omniauth-google-oauth2 (~> 0.4.1) omniauth-google-oauth2 (~> 0.4.1)
omniauth-kerberos (~> 0.3.0) omniauth-kerberos (~> 0.3.0)
omniauth-saml (~> 1.7.0) omniauth-saml (~> 1.7.0)
...@@ -938,7 +938,7 @@ DEPENDENCIES ...@@ -938,7 +938,7 @@ DEPENDENCIES
redcarpet (~> 3.3.3) redcarpet (~> 3.3.3)
redis (~> 3.2) redis (~> 3.2)
redis-namespace (~> 1.5.2) redis-namespace (~> 1.5.2)
redis-rails (~> 4.0.0) redis-rails (~> 5.0.1)
request_store (~> 1.3) request_store (~> 1.3)
rerun (~> 0.11.0) rerun (~> 0.11.0)
responders (~> 2.0) responders (~> 2.0)
...@@ -994,4 +994,4 @@ DEPENDENCIES ...@@ -994,4 +994,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.13.5 1.13.6
...@@ -2,6 +2,19 @@ ...@@ -2,6 +2,19 @@
$(() => { $(() => {
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
$(document).off('created.label').on('created.label', (e, label) => {
Store.new({
title: label.title,
position: Store.state.lists.length - 2,
list_type: 'label',
label: {
id: label.id,
title: label.title,
color: label.color
}
});
});
$('.js-new-board-list').each(function () { $('.js-new-board-list').each(function () {
const $this = $(this); const $this = $(this);
new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path')); new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path'));
......
...@@ -115,6 +115,8 @@ ...@@ -115,6 +115,8 @@
.show(); .show();
} else { } else {
this.$dropdownBack.trigger('click'); this.$dropdownBack.trigger('click');
$(document).trigger('created.label', label);
} }
}); });
} }
......
/* eslint-disable */ /* global Element */
Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatches; /* eslint-disable consistent-return, max-len */
Element.prototype.matches = Element.prototype.matches || Element.prototype.msMatchesSelector;
Element.prototype.closest = function closest(selector, selectedElement = this) { Element.prototype.closest = function closest(selector, selectedElement = this) {
if (!selectedElement) return; if (!selectedElement) return;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
Issuable.initSearch(); Issuable.initSearch();
Issuable.initChecks(); Issuable.initChecks();
Issuable.initResetFilters(); Issuable.initResetFilters();
Issuable.resetIncomingEmailToken();
return Issuable.initLabelFilterRemove(); return Issuable.initLabelFilterRemove();
}, },
initTemplates: function() { initTemplates: function() {
...@@ -154,6 +155,27 @@ ...@@ -154,6 +155,27 @@
this.issuableBulkActions.willUpdateLabels = false; this.issuableBulkActions.willUpdateLabels = false;
} }
return true; return true;
},
resetIncomingEmailToken: function() {
$('.incoming-email-token-reset').on('click', function(e) {
e.preventDefault();
$.ajax({
type: 'PUT',
url: $('.incoming-email-token-reset').attr('href'),
dataType: 'json',
success: function(response) {
$('#issue_email').val(response.new_issue_address).focus();
},
beforeSend: function() {
$('.incoming-email-token-reset').text('resetting...');
},
complete: function() {
$('.incoming-email-token-reset').text('reset it');
}
});
});
} }
}; };
......
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
(function() { (function() {
$(function() { $(function() {
if (!$(".network-graph").length) return;
var network_graph; var network_graph;
network_graph = new Network({ network_graph = new Network({
url: $(".network-graph").attr('data-url'), url: $(".network-graph").attr('data-url'),
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
margin-right: $margin-right; margin-right: $margin-right;
} }
.avatar-container { .avatar-circle {
float: left; float: left;
margin-right: 15px; margin-right: 15px;
border-radius: $avatar_radius; border-radius: $avatar_radius;
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
} }
.avatar { .avatar {
@extend .avatar-container; @extend .avatar-circle;
width: 40px; width: 40px;
height: 40px; height: 40px;
padding: 0; padding: 0;
...@@ -64,8 +64,8 @@ ...@@ -64,8 +64,8 @@
&.s160 { font-size: 96px; line-height: 158px; } &.s160 { font-size: 96px; line-height: 158px; }
} }
.image-container { .avatar-container {
@extend .avatar-container; @extend .avatar-circle;
overflow: hidden; overflow: hidden;
display: flex; display: flex;
...@@ -76,4 +76,4 @@ ...@@ -76,4 +76,4 @@
margin: 0; margin: 0;
align-self: center; align-self: center;
} }
} }
\ No newline at end of file
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
} }
.select2-highlighted { .select2-highlighted {
background: #3084bb !important; background: $gl-link-color !important;
} }
.select2-results li.select2-result-with-children > .select2-result-label { .select2-results li.select2-result-with-children > .select2-result-label {
......
...@@ -103,7 +103,7 @@ $gl-text-color-light: #8c8c8c; ...@@ -103,7 +103,7 @@ $gl-text-color-light: #8c8c8c;
$gl-text-green: #4a2; $gl-text-green: #4a2;
$gl-text-red: #d12f19; $gl-text-red: #d12f19;
$gl-text-orange: #d90; $gl-text-orange: #d90;
$gl-link-color: #3084bb; $gl-link-color: #3777b0;
$gl-dark-link-color: #333; $gl-dark-link-color: #333;
$gl-placeholder-color: #8f8f8f; $gl-placeholder-color: #8f8f8f;
$gl-icon-color: $gl-placeholder-color; $gl-icon-color: $gl-placeholder-color;
...@@ -197,7 +197,7 @@ $line-number-new: #ddfbe6; ...@@ -197,7 +197,7 @@ $line-number-new: #ddfbe6;
$line-number-select: #fbf2da; $line-number-select: #fbf2da;
$match-line: $gray-light; $match-line: $gray-light;
$table-border-gray: #f0f0f0; $table-border-gray: #f0f0f0;
$line-target-blue: #eaf3fc; $line-target-blue: #f6faff;
$line-select-yellow: #fcf8e7; $line-select-yellow: #fcf8e7;
$line-select-yellow-dark: #f0e2bd; $line-select-yellow-dark: #f0e2bd;
......
...@@ -36,9 +36,42 @@ ...@@ -36,9 +36,42 @@
padding: 10px 0; padding: 10px 0;
margin-bottom: 0; margin-bottom: 0;
.commit-options-dropdown-caret { @media (min-width: $screen-sm-min) {
@media (max-width: $screen-sm) { display: flex;
margin-left: 0; align-items: center;
.commit-meta {
flex: 1;
}
}
.commit-hash-full {
@media (max-width: $screen-sm-max) {
width: 80px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: bottom;
}
}
.commit-action-buttons {
i {
color: $gl-icon-color;
font-size: 13px;
margin-right: 3px;
}
@media (max-width: $screen-xs-max) {
.dropdown {
width: 100%;
margin-top: 10px;
}
.dropdown-toggle {
width: 100%;
}
} }
} }
} }
...@@ -188,17 +221,6 @@ ...@@ -188,17 +221,6 @@
} }
} }
.commit-action-buttons {
position: relative;
top: -1px;
i {
color: $gl-icon-color;
font-size: 13px;
margin-right: 3px;
}
}
/* /*
* Commit message textarea for web editor and * Commit message textarea for web editor and
* custom merge request message * custom merge request message
......
...@@ -23,6 +23,10 @@ ...@@ -23,6 +23,10 @@
color: $md-link-color; color: $md-link-color;
} }
.private-tokens-reset div.reset-action:not(:first-child) {
padding-top: 15px;
}
.oauth-buttons { .oauth-buttons {
.btn-group { .btn-group {
margin-right: 10px; margin-right: 10px;
......
...@@ -117,6 +117,11 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -117,6 +117,11 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:send_user_confirmation_email, :send_user_confirmation_email,
:container_registry_token_expire_delay, :container_registry_token_expire_delay,
:enabled_git_access_protocol, :enabled_git_access_protocol,
:housekeeping_enabled,
:housekeeping_bitmaps_enabled,
:housekeeping_incremental_repack_period,
:housekeeping_full_repack_period,
:housekeeping_gc_period,
repository_storages: [], repository_storages: [],
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [], import_sources: [],
......
...@@ -26,7 +26,15 @@ class ProfilesController < Profiles::ApplicationController ...@@ -26,7 +26,15 @@ class ProfilesController < Profiles::ApplicationController
def reset_private_token def reset_private_token
if current_user.reset_authentication_token! if current_user.reset_authentication_token!
flash[:notice] = "Token was successfully updated" flash[:notice] = "Private token was successfully reset"
end
redirect_to profile_account_path
end
def reset_incoming_email_token
if current_user.reset_incoming_email_token!
flash[:notice] = "Incoming email token was successfully reset"
end end
redirect_to profile_account_path redirect_to profile_account_path
......
...@@ -5,17 +5,29 @@ class Projects::NetworkController < Projects::ApplicationController ...@@ -5,17 +5,29 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :assign_ref_vars before_action :assign_ref_vars
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :assign_commit
def show def show
@url = namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json)) @url = namespace_project_network_path(@project.namespace, @project, @ref, @options.merge(format: :json))
@commit_url = namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s") @commit_url = namespace_project_commit_path(@project.namespace, @project, 'ae45ca32').gsub("ae45ca32", "%s")
respond_to do |format| respond_to do |format|
format.html format.html do
if @options[:extended_sha1] && !@commit
flash.now[:alert] = "Git revision '#{@options[:extended_sha1]}' does not exist."
end
end
format.json do format.json do
@graph = Network::Graph.new(project, @ref, @commit, @options[:filter_ref]) @graph = Network::Graph.new(project, @ref, @commit, @options[:filter_ref])
end end
end end
end end
def assign_commit
return if params[:extended_sha1].blank?
@options[:extended_sha1] = params[:extended_sha1]
@commit = @repo.commit(@options[:extended_sha1])
end
end end
...@@ -2,9 +2,9 @@ class ProjectsController < Projects::ApplicationController ...@@ -2,9 +2,9 @@ class ProjectsController < Projects::ApplicationController
include IssuableCollections include IssuableCollections
include ExtractsPath include ExtractsPath
before_action :authenticate_user!, except: [:show, :activity, :refs] before_action :authenticate_user!, except: [:index, :show, :activity, :refs]
before_action :project, except: [:new, :create] before_action :project, except: [:index, :new, :create]
before_action :repository, except: [:new, :create] before_action :repository, except: [:index, :new, :create]
before_action :assign_ref_vars, only: [:show], if: :repo_exists? before_action :assign_ref_vars, only: [:show], if: :repo_exists?
before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?] before_action :tree, only: [:show], if: [:repo_exists?, :project_view_files?]
...@@ -160,6 +160,13 @@ class ProjectsController < Projects::ApplicationController ...@@ -160,6 +160,13 @@ class ProjectsController < Projects::ApplicationController
end end
end end
def new_issue_address
return render_404 unless Gitlab::IncomingEmail.supports_issue_creation?
current_user.reset_incoming_email_token!
render json: { new_issue_address: @project.new_issue_address(current_user) }
end
def archive def archive
return access_denied! unless can?(current_user, :archive_project, @project) return access_denied! unless can?(current_user, :archive_project, @project)
......
...@@ -16,7 +16,7 @@ class SearchController < ApplicationController ...@@ -16,7 +16,7 @@ class SearchController < ApplicationController
@group = nil unless can?(current_user, :read_group, @group) @group = nil unless can?(current_user, :read_group, @group)
end end
return if params[:search].nil? || params[:search].blank? return if params[:search].blank?
@search_term = params[:search] @search_term = params[:search]
......
module AccountsHelper
def incoming_email_token_enabled?
current_user.incoming_email_token && Gitlab::IncomingEmail.supports_issue_creation?
end
end
...@@ -179,33 +179,6 @@ module BlobHelper ...@@ -179,33 +179,6 @@ module BlobHelper
} }
end end
def selected_template(issuable)
templates = issuable_templates(issuable)
params[:issuable_template] if templates.include?(params[:issuable_template])
end
def can_add_template?(issuable)
names = issuable_templates(issuable)
names.empty? && can?(current_user, :push_code, @project) && !@project.private?
end
def merge_request_template_names
@merge_request_templates ||= Gitlab::Template::MergeRequestTemplate.dropdown_names(ref_project)
end
def issue_template_names
@issue_templates ||= Gitlab::Template::IssueTemplate.dropdown_names(ref_project)
end
def issuable_templates(issuable)
@issuable_templates ||=
if issuable.is_a?(Issue)
issue_template_names
elsif issuable.is_a?(MergeRequest)
merge_request_template_names
end
end
def ref_project def ref_project
@ref_project ||= @target_project || @project @ref_project ||= @target_project || @project
end end
......
module ComponentsHelper
def gitlab_workhorse_version
if request.headers['Gitlab-Workhorse'].present?
request.headers['Gitlab-Workhorse'].split('-').first
else
Gitlab::Workhorse.version
end
end
end
...@@ -30,6 +30,33 @@ module IssuablesHelper ...@@ -30,6 +30,33 @@ module IssuablesHelper
end end
end end
def can_add_template?(issuable)
names = issuable_templates(issuable)
names.empty? && can?(current_user, :push_code, @project) && !@project.private?
end
def template_dropdown_tag(issuable, &block)
title = selected_template(issuable) || "Choose a template"
options = {
toggle_class: 'js-issuable-selector',
title: title,
filter: true,
placeholder: 'Filter',
footer_content: true,
data: {
data: issuable_templates(issuable),
field_name: 'issuable_template',
selected: selected_template(issuable),
project_path: ref_project.path,
namespace_path: ref_project.namespace.path
}
}
dropdown_tag(title, options: options) do
capture(&block)
end
end
def user_dropdown_label(user_id, default_label) def user_dropdown_label(user_id, default_label)
return default_label if user_id.nil? return default_label if user_id.nil?
return "Unassigned" if user_id == "0" return "Unassigned" if user_id == "0"
...@@ -153,4 +180,28 @@ module IssuablesHelper ...@@ -153,4 +180,28 @@ module IssuablesHelper
hexdigest(['issuables_count', issuable_type, opts.sort].flatten.join('-')) hexdigest(['issuables_count', issuable_type, opts.sort].flatten.join('-'))
end end
def issuable_templates(issuable)
@issuable_templates ||=
case issuable
when Issue
issue_template_names
when MergeRequest
merge_request_template_names
else
raise 'Unknown issuable type!'
end
end
def merge_request_template_names
@merge_request_templates ||= Gitlab::Template::MergeRequestTemplate.dropdown_names(ref_project)
end
def issue_template_names
@issue_templates ||= Gitlab::Template::IssueTemplate.dropdown_names(ref_project)
end
def selected_template(issuable)
params[:issuable_template] if issuable_templates(issuable).include?(params[:issuable_template])
end
end end
...@@ -61,6 +61,10 @@ module TodosHelper ...@@ -61,6 +61,10 @@ module TodosHelper
} }
end end
def todos_filter_empty?
todos_filter_params.values.none?
end
def todos_filter_path(options = {}) def todos_filter_path(options = {})
without = options.delete(:without) without = options.delete(:without)
......
class BaseMailer < ActionMailer::Base class BaseMailer < ActionMailer::Base
add_template_helper ApplicationHelper helper ApplicationHelper
add_template_helper GitlabMarkdownHelper helper GitlabMarkdownHelper
attr_accessor :current_user attr_accessor :current_user
helper_method :current_user, :can? helper_method :current_user, :can?
......
...@@ -10,12 +10,12 @@ class Notify < BaseMailer ...@@ -10,12 +10,12 @@ class Notify < BaseMailer
include Emails::Pipelines include Emails::Pipelines
include Emails::Members include Emails::Members
add_template_helper MergeRequestsHelper helper MergeRequestsHelper
add_template_helper DiffHelper helper DiffHelper
add_template_helper BlobHelper helper BlobHelper
add_template_helper EmailsHelper helper EmailsHelper
add_template_helper MembersHelper helper MembersHelper
add_template_helper GitlabRoutingHelper helper GitlabRoutingHelper
def test_email(recipient_email, subject, body) def test_email(recipient_email, subject, body)
mail(to: recipient_email, mail(to: recipient_email,
......
...@@ -85,6 +85,18 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -85,6 +85,18 @@ class ApplicationSetting < ActiveRecord::Base
presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' }, presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' },
if: :domain_blacklist_enabled? if: :domain_blacklist_enabled?
validates :housekeeping_incremental_repack_period,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :housekeeping_full_repack_period,
presence: true,
numericality: { only_integer: true, greater_than: :housekeeping_incremental_repack_period }
validates :housekeeping_gc_period,
presence: true,
numericality: { only_integer: true, greater_than: :housekeeping_full_repack_period }
validates_each :restricted_visibility_levels do |record, attr, value| validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil? unless value.nil?
value.each do |level| value.each do |level|
...@@ -168,6 +180,11 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -168,6 +180,11 @@ class ApplicationSetting < ActiveRecord::Base
container_registry_token_expire_delay: 5, container_registry_token_expire_delay: 5,
repository_storages: ['default'], repository_storages: ['default'],
user_default_external: false, user_default_external: false,
housekeeping_enabled: true,
housekeeping_bitmaps_enabled: true,
housekeeping_incremental_repack_period: 10,
housekeeping_full_repack_period: 50,
housekeeping_gc_period: 200,
) )
end end
...@@ -202,11 +219,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -202,11 +219,7 @@ class ApplicationSetting < ActiveRecord::Base
end end
def repository_storages def repository_storages
value = read_attribute(:repository_storages) Array(read_attribute(:repository_storages))
value = [value] if value.is_a?(String)
value = [] if value.nil?
value
end end
# repository_storage is still required in the API. Remove in 9.0 # repository_storage is still required in the API. Remove in 9.0
......
...@@ -286,6 +286,11 @@ module Issuable ...@@ -286,6 +286,11 @@ module Issuable
false false
end end
def assignee_or_author?(user)
# We're comparing IDs here so we don't need to load any associations.
author_id == user.id || assignee_id == user.id
end
def record_metrics def record_metrics
metrics = self.metrics || create_metrics metrics = self.metrics || create_metrics
metrics.record! metrics.record!
......
...@@ -4,17 +4,21 @@ module TokenAuthenticatable ...@@ -4,17 +4,21 @@ module TokenAuthenticatable
private private
def write_new_token(token_field) def write_new_token(token_field)
new_token = generate_token(token_field) new_token = generate_available_token(token_field)
write_attribute(token_field, new_token) write_attribute(token_field, new_token)
end end
def generate_token(token_field) def generate_available_token(token_field)
loop do loop do
token = Devise.friendly_token token = generate_token(token_field)
break token unless self.class.unscoped.find_by(token_field => token) break token unless self.class.unscoped.find_by(token_field => token)
end end
end end
def generate_token(token_field)
Devise.friendly_token
end
class_methods do class_methods do
def authentication_token_fields def authentication_token_fields
@token_fields || [] @token_fields || []
......
...@@ -29,6 +29,15 @@ class ExternalIssue ...@@ -29,6 +29,15 @@ class ExternalIssue
@project @project
end end
def project_id
@project.id
end
# Pattern used to extract `JIRA-123` issue references from text
def self.reference_pattern
@reference_pattern ||= %r{(?<issue>\b([A-Z][A-Z0-9_]+-)\d+)}
end
def to_reference(_from_project = nil) def to_reference(_from_project = nil)
id id
end end
......
# IssueCollection can be used to reduce a list of issues down to a subset.
#
# IssueCollection is not meant to be some sort of Enumerable, instead it's meant
# to take a list of issues and return a new list of issues based on some
# criteria. For example, given a list of issues you may want to return a list of
# issues that can be read or updated by a given user.
class IssueCollection
attr_reader :collection
def initialize(collection)
@collection = collection
end
# Returns all the issues that can be updated by the user.
def updatable_by_user(user)
return collection if user.admin?
# Given all the issue projects we get a list of projects that the current
# user has at least reporter access to.
projects_with_reporter_access = user.
projects_with_reporter_access_limited_to(project_ids).
pluck(:id)
collection.select do |issue|
if projects_with_reporter_access.include?(issue.project_id)
true
elsif issue.is_a?(Issue)
issue.assignee_or_author?(user)
else
false
end
end
end
alias_method :visible_to, :updatable_by_user
private
def project_ids
@project_ids ||= collection.map(&:project_id).uniq
end
end
...@@ -624,13 +624,12 @@ class Project < ActiveRecord::Base ...@@ -624,13 +624,12 @@ class Project < ActiveRecord::Base
end end
def new_issue_address(author) def new_issue_address(author)
# This feature is disabled for the time being. return unless Gitlab::IncomingEmail.supports_issue_creation? && author
return nil
if Gitlab::IncomingEmail.enabled? && author # rubocop:disable Lint/UnreachableCode author.ensure_incoming_email_token!
Gitlab::IncomingEmail.reply_address(
"#{path_with_namespace}+#{author.authentication_token}") Gitlab::IncomingEmail.reply_address(
end "#{path_with_namespace}+#{author.incoming_email_token}")
end end
def build_commit_note(commit) def build_commit_note(commit)
......
...@@ -163,6 +163,21 @@ class JiraService < IssueTrackerService ...@@ -163,6 +163,21 @@ class JiraService < IssueTrackerService
add_comment(data, issue_key) add_comment(data, issue_key)
end end
# reason why service cannot be tested
def disabled_title
"Please fill in Password and Username."
end
def can_test?
username.present? && password.present?
end
# JIRA does not need test data.
# We are requesting the project that belongs to the project key.
def test_data(user = nil, project = nil)
nil
end
def test_settings def test_settings
return unless url.present? return unless url.present?
# Test settings by getting the project # Test settings by getting the project
......
...@@ -1064,6 +1064,10 @@ class Repository ...@@ -1064,6 +1064,10 @@ class Repository
end end
def search_files(query, ref) def search_files(query, ref)
unless exists? && has_visible_content? && query.present?
return []
end
offset = 2 offset = 2
args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
......
...@@ -13,6 +13,7 @@ class User < ActiveRecord::Base ...@@ -13,6 +13,7 @@ class User < ActiveRecord::Base
DEFAULT_NOTIFICATION_LEVEL = :participating DEFAULT_NOTIFICATION_LEVEL = :participating
add_authentication_token_field :authentication_token add_authentication_token_field :authentication_token
add_authentication_token_field :incoming_email_token
default_value_for :admin, false default_value_for :admin, false
default_value_for(:external) { current_application_settings.user_default_external } default_value_for(:external) { current_application_settings.user_default_external }
...@@ -119,7 +120,7 @@ class User < ActiveRecord::Base ...@@ -119,7 +120,7 @@ class User < ActiveRecord::Base
before_validation :set_public_email, if: ->(user) { user.public_email_changed? } before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? } after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? }
before_save :ensure_authentication_token before_save :ensure_authentication_token, :ensure_incoming_email_token
before_save :ensure_external_user_rights before_save :ensure_external_user_rights
after_save :ensure_namespace_correct after_save :ensure_namespace_correct
after_initialize :set_projects_limit after_initialize :set_projects_limit
...@@ -444,6 +445,16 @@ class User < ActiveRecord::Base ...@@ -444,6 +445,16 @@ class User < ActiveRecord::Base
Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})") Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})")
end end
# Returns the projects this user has reporter (or greater) access to, limited
# to at most the given projects.
#
# This method is useful when you have a list of projects and want to
# efficiently check to which of these projects the user has at least reporter
# access.
def projects_with_reporter_access_limited_to(projects)
authorized_projects(Gitlab::Access::REPORTER).where(id: projects)
end
def viewable_starred_projects def viewable_starred_projects
starred_projects.where("projects.visibility_level IN (?) OR projects.id IN (#{projects_union.to_sql})", starred_projects.where("projects.visibility_level IN (?) OR projects.id IN (#{projects_union.to_sql})",
[Project::PUBLIC, Project::INTERNAL]) [Project::PUBLIC, Project::INTERNAL])
...@@ -946,4 +957,13 @@ class User < ActiveRecord::Base ...@@ -946,4 +957,13 @@ class User < ActiveRecord::Base
signup_domain =~ regexp signup_domain =~ regexp
end end
end end
def generate_token(token_field)
if token_field == :incoming_email_token
# Needs to be all lowercase and alphanumeric because it's gonna be used in an email address.
SecureRandom.hex.to_i(16).to_s(36)
else
super
end
end
end end
...@@ -4,7 +4,7 @@ class IssuablePolicy < BasePolicy ...@@ -4,7 +4,7 @@ class IssuablePolicy < BasePolicy
end end
def rules def rules
if @user && (@subject.author == @user || @subject.assignee == @user) if @user && @subject.assignee_or_author?(@user)
can! :"read_#{action_name}" can! :"read_#{action_name}"
can! :"update_#{action_name}" can! :"update_#{action_name}"
end end
......
...@@ -8,9 +8,8 @@ class IssuePolicy < IssuablePolicy ...@@ -8,9 +8,8 @@ class IssuePolicy < IssuablePolicy
if @subject.confidential? && !can_read_confidential? if @subject.confidential? && !can_read_confidential?
cannot! :read_issue cannot! :read_issue
cannot! :admin_issue
cannot! :update_issue cannot! :update_issue
cannot! :read_issue cannot! :admin_issue
end end
end end
...@@ -18,11 +17,7 @@ class IssuePolicy < IssuablePolicy ...@@ -18,11 +17,7 @@ class IssuePolicy < IssuablePolicy
def can_read_confidential? def can_read_confidential?
return false unless @user return false unless @user
return true if @user.admin?
return true if @subject.author == @user
return true if @subject.assignee == @user
return true if @subject.project.team.member?(@user, Gitlab::Access::REPORTER)
false IssueCollection.new([@subject]).visible_to(@user).any?
end end
end end
...@@ -105,35 +105,11 @@ class GitPushService < BaseService ...@@ -105,35 +105,11 @@ class GitPushService < BaseService
# Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched, # Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched,
# close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables. # close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables.
def process_commit_messages def process_commit_messages
is_default_branch = is_default_branch? default = is_default_branch?
authors = Hash.new do |hash, commit|
email = commit.author_email
next hash[email] if hash.has_key?(email)
hash[email] = commit_user(commit)
end
@push_commits.each do |commit| @push_commits.each do |commit|
# Keep track of the issues that will be actually closed because they are on a default branch. ProcessCommitWorker.
# Hence, when creating cross-reference notes, the not-closed issues (on non-default branches) perform_async(project.id, current_user.id, commit.id, default)
# will also have cross-reference.
closed_issues = []
if is_default_branch
# Close issues if these commits were pushed to the project's default branch and the commit message matches the
# closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to
# a different branch.
closed_issues = commit.closes_issues(current_user)
closed_issues.each do |issue|
if can?(current_user, :update_issue, issue)
Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit: commit)
end
end
end
commit.create_cross_references!(authors[commit], closed_issues)
update_issue_metrics(commit, authors)
end end
end end
...@@ -176,11 +152,4 @@ class GitPushService < BaseService ...@@ -176,11 +152,4 @@ class GitPushService < BaseService
def branch_name def branch_name
@branch_name ||= Gitlab::Git.ref_name(params[:ref]) @branch_name ||= Gitlab::Git.ref_name(params[:ref])
end end
def update_issue_metrics(commit, authors)
mentioned_issues = commit.all_references(authors[commit]).issues
Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil).
update_all(first_mentioned_in_commit_at: commit.committed_date)
end
end end
module Issues module Issues
class CloseService < Issues::BaseService class CloseService < Issues::BaseService
# Closes the supplied issue if the current user is able to do so.
def execute(issue, commit: nil, notifications: true, system_note: true) def execute(issue, commit: nil, notifications: true, system_note: true)
return issue unless can?(current_user, :update_issue, issue) return issue unless can?(current_user, :update_issue, issue)
close_issue(issue,
commit: commit,
notifications: notifications,
system_note: system_note)
end
# Closes the supplied issue without checking if the user is authorized to
# do so.
#
# The code calling this method is responsible for ensuring that a user is
# allowed to close the given issue.
def close_issue(issue, commit: nil, notifications: true, system_note: true)
if project.jira_tracker? && project.jira_service.active if project.jira_tracker? && project.jira_service.active
project.jira_service.execute(commit, issue) project.jira_service.execute(commit, issue)
todo_service.close_issue(issue, current_user) todo_service.close_issue(issue, current_user)
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
# #
module Projects module Projects
class HousekeepingService < BaseService class HousekeepingService < BaseService
include Gitlab::CurrentSettings
LEASE_TIMEOUT = 3600 LEASE_TIMEOUT = 3600
class LeaseTaken < StandardError class LeaseTaken < StandardError
...@@ -20,13 +22,14 @@ module Projects ...@@ -20,13 +22,14 @@ module Projects
end end
def execute def execute
raise LeaseTaken unless try_obtain_lease lease_uuid = try_obtain_lease
raise LeaseTaken unless lease_uuid.present?
execute_gitlab_shell_gc execute_gitlab_shell_gc(lease_uuid)
end end
def needed? def needed?
@project.pushes_since_gc >= 10 pushes_since_gc > 0 && period_match? && housekeeping_enabled?
end end
def increment! def increment!
...@@ -37,19 +40,59 @@ module Projects ...@@ -37,19 +40,59 @@ module Projects
private private
def execute_gitlab_shell_gc def execute_gitlab_shell_gc(lease_uuid)
GitGarbageCollectWorker.perform_async(@project.id) GitGarbageCollectWorker.perform_async(@project.id, task, lease_key, lease_uuid)
ensure ensure
Gitlab::Metrics.measure(:reset_pushes_since_gc) do if pushes_since_gc >= gc_period
@project.reset_pushes_since_gc Gitlab::Metrics.measure(:reset_pushes_since_gc) do
@project.reset_pushes_since_gc
end
end end
end end
def try_obtain_lease def try_obtain_lease
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
lease = ::Gitlab::ExclusiveLease.new("project_housekeeping:#{@project.id}", timeout: LEASE_TIMEOUT) lease = ::Gitlab::ExclusiveLease.new(lease_key, timeout: LEASE_TIMEOUT)
lease.try_obtain lease.try_obtain
end end
end end
def lease_key
"project_housekeeping:#{@project.id}"
end
def pushes_since_gc
@project.pushes_since_gc
end
def task
if pushes_since_gc % gc_period == 0
:gc
elsif pushes_since_gc % full_repack_period == 0
:full_repack
else
:incremental_repack
end
end
def period_match?
[gc_period, full_repack_period, repack_period].any? { |period| pushes_since_gc % period == 0 }
end
def housekeeping_enabled?
current_application_settings.housekeeping_enabled
end
def gc_period
current_application_settings.housekeeping_gc_period
end
def full_repack_period
current_application_settings.housekeeping_full_repack_period
end
def repack_period
current_application_settings.housekeeping_incremental_repack_period
end
end end
end end
...@@ -422,5 +422,44 @@ ...@@ -422,5 +422,44 @@
Enable this option to include the name of the author of the issue, Enable this option to include the name of the author of the issue,
merge request or comment in the email body instead. merge request or comment in the email body instead.
%fieldset
%legend Automatic Git repository housekeeping
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :housekeeping_enabled do
= f.check_box :housekeeping_enabled
Enable automatic repository housekeeping (git repack, git gc)
.help-block
If you keep automatic housekeeping disabled for a long time Git
repository access on your GitLab server will become slower and your
repositories will use more disk space. We recommend to always leave
this enabled.
.checkbox
= f.label :housekeeping_bitmaps_enabled do
= f.check_box :housekeeping_bitmaps_enabled
Enable Git pack file bitmap creation
.help-block
Creating pack file bitmaps makes housekeeping take a little longer but
bitmaps should accelerate 'git clone' performance.
.form-group
= f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :housekeeping_incremental_repack_period, class: 'form-control'
.help-block
Number of Git pushes after which an incremental 'git repack' is run.
.form-group
= f.label :housekeeping_full_repack_period, 'Full repack period', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :housekeeping_full_repack_period, class: 'form-control'
.help-block
Number of Git pushes after which a full 'git repack' is run.
.form-group
= f.label :housekeeping_gc_period, 'Git GC period', class: 'control-label col-sm-2'
.col-sm-10
= f.number_field :housekeeping_gc_period, class: 'form-control'
.help-block
Number of Git pushes after which 'git gc' is run.
.form-actions .form-actions
= f.submit 'Save', class: 'btn btn-save' = f.submit 'Save', class: 'btn btn-save'
...@@ -87,7 +87,7 @@ ...@@ -87,7 +87,7 @@
%p %p
GitLab Workhorse GitLab Workhorse
%span.pull-right %span.pull-right
= Gitlab::Workhorse.version = gitlab_workhorse_version
%p %p
GitLab API GitLab API
%span.pull-right %span.pull-right
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)} %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
= visibility_level_icon(group.visibility_level, fw: false) = visibility_level_icon(group.visibility_level, fw: false)
.image-container.s40 .avatar-container.s40
= image_tag group_icon(group), class: "avatar s40 hidden-xs" = image_tag group_icon(group), class: "avatar s40 hidden-xs"
.title .title
= link_to [:admin, group], class: 'group-name' do = link_to [:admin, group], class: 'group-name' do
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
Group info: Group info:
%ul.well-list %ul.well-list
%li %li
.image-container.s60 .avatar-container.s60
= image_tag group_icon(@group), class: "avatar s60" = image_tag group_icon(@group), class: "avatar s60"
%li %li
%span.light Name: %span.light Name:
......
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
.title .title
= link_to [:admin, project.namespace.becomes(Namespace), project] do = link_to [:admin, project.namespace.becomes(Namespace), project] do
.dash-project-avatar .dash-project-avatar
.image-container.s40 .avatar-container.s40
= project_icon(project, alt: '', class: 'avatar project-avatar s40') = project_icon(project, alt: '', class: 'avatar project-avatar s40')
%span.project-full-name %span.project-full-name
%span.namespace-name %span.namespace-name
......
...@@ -82,15 +82,19 @@ ...@@ -82,15 +82,19 @@
- elsif current_user.todos.any? - elsif current_user.todos.any?
.todos-all-done .todos-all-done
= render "shared/empty_states/todos_all_done.svg" = render "shared/empty_states/todos_all_done.svg"
%h4.text-center - if todos_filter_empty?
Good job! Looks like you don't have any todos left. %h4.text-center
%p.text-center Good job! Looks like you don't have any todos left.
Are you looking for things to do? Take a look at %p.text-center
= succeed "," do Are you looking for things to do? Take a look at
= link_to "the opened issues", issues_dashboard_path = succeed "," do
contribute to = link_to "the opened issues", issues_dashboard_path
= link_to "merge requests", merge_requests_dashboard_path contribute to
or mention someone in a comment to assign a new todo automatically. = link_to "merge requests", merge_requests_dashboard_path
or mention someone in a comment to assign a new todo automatically.
- else
%h4.text-center
There are no todos to show.
- else - else
.todos-empty .todos-empty
.todos-empty-hero .todos-empty-hero
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.image-container.s160 .avatar-container.s160
= image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160' = image_tag group_icon(@group), alt: '', class: 'avatar group-avatar s160'
%p.light %p.light
- if @group.avatar? - if @group.avatar?
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.cover-block.groups-cover-block .cover-block.groups-cover-block
%div{ class: container_class } %div{ class: container_class }
.image-container.s70.group-avatar .avatar-container.s70.group-avatar
= image_tag group_icon(@group), class: "avatar s70 avatar-tile" = image_tag group_icon(@group), class: "avatar s70 avatar-tile"
.group-info .group-info
.cover-title .cover-title
......
...@@ -103,11 +103,11 @@ ...@@ -103,11 +103,11 @@
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
%img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/ %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
%a{href: commit_url(@pipeline), style: "color:#3084bb;text-decoration:none;"} %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
= @pipeline.short_sha = @pipeline.short_sha
- if @merge_request - if @merge_request
in in
%a{href: merge_request_url(@merge_request), style: "color:#3084bb;text-decoration:none;"} %a{href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;"}
= @merge_request.to_reference = @merge_request.to_reference
.commit{style: "color:#5c5c5c;font-weight:300;"} .commit{style: "color:#5c5c5c;font-weight:300;"}
= @pipeline.git_commit_message.truncate(50) = @pipeline.git_commit_message.truncate(50)
...@@ -134,7 +134,7 @@ ...@@ -134,7 +134,7 @@
%tr.pre-section %tr.pre-section
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333333;font-size:15px;font-weight:400;line-height:1.4;padding:15px 0;"}
Pipeline Pipeline
%a{href: pipeline_url(@pipeline), style: "color:#3084bb;text-decoration:none;"} %a{href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
= "\##{@pipeline.id}" = "\##{@pipeline.id}"
had had
= failed.size = failed.size
...@@ -158,7 +158,7 @@ ...@@ -158,7 +158,7 @@
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#8c8c8c;font-weight:500;font-size:15px;vertical-align:middle;"}
= build.stage = build.stage
%td{align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;"} %td{align: "right", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:20px 0;color:#8c8c8c;font-weight:500;font-size:15px;"}
%a{href: pipeline_build_url(@pipeline, build), style: "color:#3084bb;text-decoration:none;"} %a{href: pipeline_build_url(@pipeline, build), style: "color:#3777b0;text-decoration:none;"}
= build.name = build.name
%tr.build-log %tr.build-log
%td{colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;"} %td{colspan: "2", style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:0 0 15px;"}
...@@ -168,10 +168,10 @@ ...@@ -168,10 +168,10 @@
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"}
%img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/ %img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/
%div %div
%a{href: profile_notifications_url, style: "color:#3084bb;text-decoration:none;"} Manage all notifications %a{href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;"} Manage all notifications
&middot; &middot;
%a{href: help_url, style: "color:#3084bb;text-decoration:none;"} Help %a{href: help_url, style: "color:#3777b0;text-decoration:none;"} Help
%div %div
You're receiving this email because of your account on You're receiving this email because of your account on
= succeed "." do = succeed "." do
%a{href: root_url, style: "color:#3084bb;text-decoration:none;"}= Gitlab.config.gitlab.host %a{href: root_url, style: "color:#3777b0;text-decoration:none;"}= Gitlab.config.gitlab.host
...@@ -103,11 +103,11 @@ ...@@ -103,11 +103,11 @@
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;"}
%img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/ %img{height: "13", src: image_url('mailers/ci_pipeline_notif_v1/icon-commit-gray.gif'), style: "display:block;", width: "13"}/
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;"}
%a{href: commit_url(@pipeline), style: "color:#3084bb;text-decoration:none;"} %a{href: commit_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
= @pipeline.short_sha = @pipeline.short_sha
- if @merge_request - if @merge_request
in in
%a{href: merge_request_url(@merge_request), style: "color:#3084bb;text-decoration:none;"} %a{href: merge_request_url(@merge_request), style: "color:#3777b0;text-decoration:none;"}
= @merge_request.to_reference = @merge_request.to_reference
.commit{style: "color:#5c5c5c;font-weight:300;"} .commit{style: "color:#5c5c5c;font-weight:300;"}
= @pipeline.git_commit_message.truncate(50) = @pipeline.git_commit_message.truncate(50)
...@@ -135,7 +135,7 @@ ...@@ -135,7 +135,7 @@
- build_count = @pipeline.statuses.latest.size - build_count = @pipeline.statuses.latest.size
- stage_count = @pipeline.stages.size - stage_count = @pipeline.stages.size
Pipeline Pipeline
%a{href: pipeline_url(@pipeline), style: "color:#3084bb;text-decoration:none;"} %a{href: pipeline_url(@pipeline), style: "color:#3777b0;text-decoration:none;"}
= "\##{@pipeline.id}" = "\##{@pipeline.id}"
successfully completed successfully completed
= "#{build_count} #{'build'.pluralize(build_count)}" = "#{build_count} #{'build'.pluralize(build_count)}"
...@@ -145,10 +145,10 @@ ...@@ -145,10 +145,10 @@
%td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"} %td{style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;"}
%img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/ %img{alt: "GitLab", height: "33", src: image_url('mailers/ci_pipeline_notif_v1/gitlab-logo-full-horizontal.gif'), style: "display:block;margin:0 auto 1em;", width: "90"}/
%div %div
%a{href: profile_notifications_url, style: "color:#3084bb;text-decoration:none;"} Manage all notifications %a{href: profile_notifications_url, style: "color:#3777b0;text-decoration:none;"} Manage all notifications
&middot; &middot;
%a{href: help_url, style: "color:#3084bb;text-decoration:none;"} Help %a{href: help_url, style: "color:#3777b0;text-decoration:none;"} Help
%div %div
You're receiving this email because of your account on You're receiving this email because of your account on
= succeed "." do = succeed "." do
%a{href: root_url, style: "color:#3084bb;text-decoration:none;"}= Gitlab.config.gitlab.host %a{href: root_url, style: "color:#3777b0;text-decoration:none;"}= Gitlab.config.gitlab.host
...@@ -8,24 +8,36 @@ ...@@ -8,24 +8,36 @@
.row.prepend-top-default .row.prepend-top-default
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
%h4.prepend-top-0 %h4.prepend-top-0
Private Token = incoming_email_token_enabled? ? "Private Tokens" : "Private Token"
%p %p
Your private token is used to access application resources without authentication. Keep
.col-lg-9 = incoming_email_token_enabled? ? "these tokens" : "this token"
= form_for @user, url: reset_private_token_profile_path, method: :put, html: { class: "private-token" } do |f| secret, anyone with access to them can interact with GitLab as if they were you.
.col-lg-9.private-tokens-reset
.reset-action
%p.cgray %p.cgray
- if current_user.private_token - if current_user.private_token
= label_tag "token", "Private token", class: "label-light" = label_tag "private-token", "Private token", class: "label-light"
= text_field_tag "token", current_user.private_token, class: "form-control" = text_field_tag "private-token", current_user.private_token, class: "form-control", readonly: true, onclick: "this.select()"
- else - else
%span You don`t have one yet. Click generate to fix it. %span You don't have one yet. Click generate to fix it.
%p.help-block %p.help-block
It can be used for atom feeds or the API. Keep it secret! Your private token is used to access the API and Atom feeds without username/password authentication.
.prepend-top-default .prepend-top-default
- if current_user.private_token - if current_user.private_token
= f.submit 'Reset private token', data: { confirm: "Are you sure?" }, class: "btn btn-default" = link_to 'Reset private token', reset_private_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default private-token"
- else - else
= f.submit 'Generate', class: "btn btn-default" = f.submit 'Generate', class: "btn btn-default"
- if incoming_email_token_enabled?
.reset-action
%p.cgray
= label_tag "incoming-email-token", "Incoming Email Token", class: 'label-light'
= text_field_tag "incoming-email-token", current_user.incoming_email_token, class: "form-control", readonly: true, onclick: "this.select()"
%p.help-block
Your incoming email token is used to create new issues by email, and is included in your project-specific email addresses.
.prepend-top-default
= link_to 'Reset incoming email token', reset_incoming_email_token_profile_path, method: :put, data: { confirm: "Are you sure?" }, class: "btn btn-default incoming-email-token"
%hr %hr
.row.prepend-top-default .row.prepend-top-default
.col-lg-3.profile-settings-sidebar .col-lg-3.profile-settings-sidebar
......
- empty_repo = @project.empty_repo? - empty_repo = @project.empty_repo?
.project-home-panel.text-center{ class: ("empty-project" if empty_repo) } .project-home-panel.text-center{ class: ("empty-project" if empty_repo) }
%div{ class: container_class } %div{ class: container_class }
.image-container.s70.project-avatar .avatar-container.s70.project-avatar
= project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile') = project_icon(@project, alt: @project.name, class: 'avatar s70 avatar-tile')
%h1.project-title %h1.project-title
= @project.name = @project.name
......
.commit-info-row.commit-info-row-header .commit-info-row.commit-info-row-header
%span.hidden-xs.hidden-sm Commit .commit-meta
= link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace js-details-short" %strong Commit
= link_to("#", class: "js-details-expand hidden-xs hidden-sm") do %strong.monospace.js-details-short= @commit.short_id
%span.text-expander = link_to("#", class: "js-details-expand hidden-xs hidden-sm") do
\... %span.text-expander
%span.js-details-content.hide \...
= link_to @commit.id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace hidden-xs hidden-sm" %span.js-details-content.hide
= clipboard_button(clipboard_text: @commit.id) %strong.monospace.commit-hash-full= @commit.id
%span.hidden-xs authored = clipboard_button(clipboard_text: @commit.id)
#{time_ago_with_tooltip(@commit.authored_date)} %span.hidden-xs authored
%span by #{time_ago_with_tooltip(@commit.authored_date)}
= author_avatar(@commit, size: 24) %span by
%strong = author_avatar(@commit, size: 24)
= commit_author_link(@commit, avatar: true, size: 24)
- if @commit.different_committer?
%span.light Committed by
%strong %strong
= commit_committer_link(@commit, avatar: true, size: 24) = commit_author_link(@commit, avatar: true, size: 24)
#{time_ago_with_tooltip(@commit.committed_date)} - if @commit.different_committer?
%span.light Committed by
.pull-right.commit-action-buttons %strong
= commit_committer_link(@commit, avatar: true, size: 24)
#{time_ago_with_tooltip(@commit.committed_date)}
.commit-action-buttons
- if defined?(@notes_count) && @notes_count > 0 - if defined?(@notes_count) && @notes_count > 0
%span.btn.disabled.btn-grouped.hidden-xs.append-right-10 %span.btn.disabled.btn-grouped.hidden-xs.append-right-10
= icon('comment') = icon('comment')
...@@ -28,8 +28,8 @@ ...@@ -28,8 +28,8 @@
Browse Files Browse Files
.dropdown.inline .dropdown.inline
%a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } } %a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } }
%span.hidden-xs Options %span Options
= icon('caret-down', class: ".commit-options-dropdown-caret") = icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right %ul.dropdown-menu.dropdown-menu-align-right
%li.visible-xs-block.visible-sm-block %li.visible-xs-block.visible-sm-block
= link_to namespace_project_tree_path(@project.namespace, @project, @commit) do = link_to namespace_project_tree_path(@project.namespace, @project, @commit) do
......
%i.fa.diff-toggle-caret %i.fa.diff-toggle-caret.fa-fw
- if defined?(blob) && blob && diff_file.submodule? - if defined?(blob) && blob && diff_file.submodule?
%span %span
= icon('archive fw') = icon('archive fw')
......
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
Project avatar Project avatar
.form-group .form-group
- if @project.avatar? - if @project.avatar?
.image-container.s160 .avatar-container.s160
= project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160') = project_icon("#{@project.namespace.to_param}/#{@project.to_param}", alt: '', class: 'avatar project-avatar s160')
%p.light %p.light
- if @project.avatar_in_git - if @project.avatar_in_git
......
...@@ -12,16 +12,23 @@ ...@@ -12,16 +12,23 @@
Create new issue by email Create new issue by email
.modal-body .modal-body
%p %p
Write an email to the below email address. (This is a private email address, so keep it secret.) You can create a new issue inside this project by sending an email to the following email address:
.email-modal-input-group.input-group .email-modal-input-group.input-group
= text_field_tag :issue_email, email, class: "monospace js-select-on-focus form-control", readonly: true = text_field_tag :issue_email, email, class: "monospace js-select-on-focus form-control", readonly: true
.input-group-btn .input-group-btn
= clipboard_button(clipboard_target: '#issue_email') = clipboard_button(clipboard_target: '#issue_email')
%p %p
Send an email to this address to create an issue. The subject will be used as the title of the new issue, and the message will be the description.
%p
Use the subject line as the title of your issue. = link_to 'Slash commands', help_page_path('user/project/slash_commands'), target: '_blank', tabindex: -1
and styling with
= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
are supported.
%p %p
Use the message as the body of your issue (feel free to include some nice This is a private email address, generated just for you.
= succeed ")." do
= link_to "Markdown", help_page_path('markdown', 'markdown') Anyone who gets ahold of it can create issues as if they were you.
You should
= link_to 'reset it', new_issue_address_namespace_project_path(@project.namespace, @project), class: 'incoming-email-token-reset'
if that ever happens.
...@@ -17,5 +17,6 @@ ...@@ -17,5 +17,6 @@
= check_box_tag :filter_ref, 1, @options[:filter_ref] = check_box_tag :filter_ref, 1, @options[:filter_ref]
%span Begin with the selected commit %span Begin with the selected commit
.network-graph{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } } - if @commit
= spinner nil, true .network-graph{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } }
= spinner nil, true
...@@ -12,6 +12,9 @@ ...@@ -12,6 +12,9 @@
= form.submit 'Save changes', class: 'btn btn-save' = form.submit 'Save changes', class: 'btn btn-save'
&nbsp; &nbsp;
- if @service.valid? && @service.activated? - if @service.valid? && @service.activated?
- disabled = @service.can_test? ? '':'disabled' - unless @service.can_test?
= link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service), class: "btn #{disabled}", title: @service.disabled_title - disabled_class = 'disabled'
- disabled_title = @service.disabled_title
= link_to 'Test settings', test_namespace_project_service_path(@project.namespace, @project, @service), class: "btn #{disabled_class}", title: disabled_title
= link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel" = link_to "Cancel", namespace_project_services_path(@project.namespace, @project), class: "btn btn-cancel"
= render 'projects/commits/commit', project: @project, commit: commit = render 'projects/commits/commit', project: @project, commit: commit, ref: nil
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
%span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)} %span.visibility-icon.has-tooltip{data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group)}
= visibility_level_icon(group.visibility_level, fw: false) = visibility_level_icon(group.visibility_level, fw: false)
.image-container.s40 .avatar-container.s40
= image_tag group_icon(group), class: "avatar s40 hidden-xs" = image_tag group_icon(group), class: "avatar s40 hidden-xs"
.title .title
= link_to group, class: 'group-name' do = link_to group, class: 'group-name' do
......
- project = @target_project || @project - project = @target_project || @project
= form_errors(issuable) = form_errors(issuable)
- if @conflict - if @conflict
...@@ -11,23 +12,9 @@ ...@@ -11,23 +12,9 @@
.form-group .form-group
= f.label :title, class: 'control-label' = f.label :title, class: 'control-label'
- issuable_template_names = issuable_templates(issuable) = render 'shared/issuable/form/template_selector', issuable: issuable
- if issuable_template_names.any? %div{ class: issuable_templates(issuable).any? ? 'col-sm-7 col-lg-8' : 'col-sm-10' }
.col-sm-3.col-lg-2
.js-issuable-selector-wrap{ data: { issuable_type: issuable.class.to_s.underscore.downcase } }
- title = selected_template(issuable) || "Choose a template"
= dropdown_tag(title, options: { toggle_class: 'js-issuable-selector',
title: title, filter: true, placeholder: 'Filter', footer_content: true,
data: { data: issuable_template_names, field_name: 'issuable_template', selected: selected_template(issuable), project_path: ref_project.path, namespace_path: ref_project.namespace.path } } ) do
%ul.dropdown-footer-list
%li
%a.no-template
No template
%a.reset-template
Reset template
%div{ class: issuable_template_names.any? ? 'col-sm-7 col-lg-8' : 'col-sm-10' }
= f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off', = f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off',
class: 'form-control pad', required: true class: 'form-control pad', required: true
......
- issuable = local_assigns.fetch(:issuable, nil)
- return unless issuable && issuable_templates(issuable).any?
.col-sm-3.col-lg-2
.js-issuable-selector-wrap{ data: { issuable_type: issuable.to_ability_name } }
= template_dropdown_tag(issuable) do
%ul.dropdown-footer-list
%li
%a.no-template
No template
%a.reset-template
Reset template
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
- if avatar - if avatar
.dash-project-avatar .dash-project-avatar
.image-container.s40 .avatar-container.s40
- if use_creator_avatar - if use_creator_avatar
= image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:'' = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
- else - else
......
.clearfix .clearfix
- groups.each do |group| - groups.each do |group|
= link_to group, class: 'profile-groups-avatars inline', title: group.name do = link_to group, class: 'profile-groups-avatars inline', title: group.name do
.image-container.s40 .avatar-container.s40
= image_tag group_icon(group), class: 'avatar group-avatar s40' = image_tag group_icon(group), class: 'avatar group-avatar s40'
class GitGarbageCollectWorker class GitGarbageCollectWorker
include Sidekiq::Worker include Sidekiq::Worker
include Gitlab::ShellAdapter
include DedicatedSidekiqQueue include DedicatedSidekiqQueue
include Gitlab::CurrentSettings
sidekiq_options retry: false sidekiq_options retry: false
def perform(project_id) def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
project = Project.find(project_id) project = Project.find(project_id)
task = task.to_sym
cmd = command(task)
repo_path = project.repository.path_to_repo
description = "'#{cmd.join(' ')}' in #{repo_path}"
Gitlab::GitLogger.info(description)
output, status = Gitlab::Popen.popen(cmd, repo_path)
Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
gitlab_shell.gc(project.repository_storage_path, project.path_with_namespace)
# Refresh the branch cache in case garbage collection caused a ref lookup to fail # Refresh the branch cache in case garbage collection caused a ref lookup to fail
flush_ref_caches(project) if task == :gc
ensure
Gitlab::ExclusiveLease.cancel(lease_key, lease_uuid) if lease_key.present? && lease_uuid.present?
end
private
def command(task)
case task
when :gc
git(write_bitmaps: bitmaps_enabled?) + %w[gc]
when :full_repack
git(write_bitmaps: bitmaps_enabled?) + %w[repack -A -d --pack-kept-objects]
when :incremental_repack
# Normal git repack fails when bitmaps are enabled. It is impossible to
# create a bitmap here anyway.
git(write_bitmaps: false) + %w[repack -d]
else
raise "Invalid gc task: #{task.inspect}"
end
end
def flush_ref_caches(project)
project.repository.after_create_branch project.repository.after_create_branch
project.repository.branch_names project.repository.branch_names
project.repository.has_visible_content? project.repository.has_visible_content?
end end
def bitmaps_enabled?
current_application_settings.housekeeping_bitmaps_enabled
end
def git(write_bitmaps:)
config_value = write_bitmaps ? 'true' : 'false'
%W[git -c repack.writeBitmaps=#{config_value}]
end
end end
# Worker for processing individiual commit messages pushed to a repository.
#
# Jobs for this worker are scheduled for every commit that is being pushed. As a
# result of this the workload of this worker should be kept to a bare minimum.
# Consider using an extra worker if you need to add any extra (and potentially
# slow) processing of commits.
class ProcessCommitWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
# project_id - The ID of the project this commit belongs to.
# user_id - The ID of the user that pushed the commit.
# commit_sha - The SHA1 of the commit to process.
# default - The data was pushed to the default branch.
def perform(project_id, user_id, commit_sha, default = false)
project = Project.find_by(id: project_id)
return unless project
user = User.find_by(id: user_id)
return unless user
commit = find_commit(project, commit_sha)
return unless commit
author = commit.author || user
process_commit_message(project, commit, user, author, default)
update_issue_metrics(commit, author)
end
def process_commit_message(project, commit, user, author, default = false)
closed_issues = default ? commit.closes_issues(user) : []
unless closed_issues.empty?
close_issues(project, user, author, commit, closed_issues)
end
commit.create_cross_references!(author, closed_issues)
end
def close_issues(project, user, author, commit, issues)
# We don't want to run permission related queries for every single issue,
# therefor we use IssueCollection here and skip the authorization check in
# Issues::CloseService#execute.
IssueCollection.new(issues).updatable_by_user(user).each do |issue|
Issues::CloseService.new(project, author).
close_issue(issue, commit: commit)
end
end
def update_issue_metrics(commit, author)
mentioned_issues = commit.all_references(author).issues
Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil).
update_all(first_mentioned_in_commit_at: commit.committed_date)
end
private
def find_commit(project, sha)
project.commit(sha)
end
end
---
title: Use the Gitlab Workhorse HTTP header in the admin dashboard
merge_request:
author: Chris Wright
---
title: Rewrite git blame spinach feature tests to rspec feature tests
merge_request: 7197
author: Lisanne Fellinger
---
title: Fix broken commits search
merge_request:
author:
---
title: Expose label IDs in API
merge_request: 7275
author: Rares Sfirlogea
---
title: Add an index for project_id in project_import_data to improve performance
merge_request:
author:
---
title: API: Ability to retrieve version information
merge_request: 7286
author: Robert Schilling
---
title: Return 400 when creating a system hook fails
merge_request: 7350
author: Robert Schilling
---
title: Fix broken link to observatory cli on Frontend Dev Guide
merge_request:
author: Sam Rose
---
title: Faster search inside Project
merge_request:
author:
---
title: Fix 404 on network page when entering non-existent git revision
merge_request: 7172
author: Hiroyuki Sato
---
title: Finer-grained Git gargage collection
merge_request: 6588
author:
---
title: Allow to test JIRA service settings without having a repository
merge_request:
author:
---
title: Process commits using a dedicated Sidekiq worker
merge_request: 6802
author:
---
title: Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2
merge_request:
author:
---
title: Set default Sidekiq retries to 3
merge_request: 7294
author:
---
title: Use separate email-token for incoming email and revert back the inactive feature
merge_request: 5914
author:
# Adds draw method into Rails routing
# It allows us to keep routing splitted into files
class ActionDispatch::Routing::Mapper
def draw(routes_name)
instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
end
end
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
redis_config_hash = Gitlab::Redis.params redis_config_hash = Gitlab::Redis.params
redis_config_hash[:namespace] = Gitlab::Redis::SIDEKIQ_NAMESPACE redis_config_hash[:namespace] = Gitlab::Redis::SIDEKIQ_NAMESPACE
# Default is to retry 25 times with exponential backoff. That's too much.
Sidekiq.default_worker_options = { retry: 3 }
Sidekiq.configure_server do |config| Sidekiq.configure_server do |config|
config.redis = redis_config_hash config.redis = redis_config_hash
......
...@@ -2,12 +2,6 @@ require 'sidekiq/web' ...@@ -2,12 +2,6 @@ require 'sidekiq/web'
require 'sidekiq/cron/web' require 'sidekiq/cron/web'
require 'api/api' require 'api/api'
class ActionDispatch::Routing::Mapper
def draw(routes_name)
instance_eval(File.read(Rails.root.join("config/routes/#{routes_name}.rb")))
end
end
Rails.application.routes.draw do Rails.application.routes.draw do
concern :access_requestable do concern :access_requestable do
post :request_access, on: :collection post :request_access, on: :collection
......
scope constraints: { id: /.+\.git/, format: nil } do
# Git HTTP clients ('git clone' etc.)
get '/info/refs', to: 'git_http#info_refs'
post '/git-upload-pack', to: 'git_http#git_upload_pack'
post '/git-receive-pack', to: 'git_http#git_receive_pack'
# Git LFS API (metadata)
post '/info/lfs/objects/batch', to: 'lfs_api#batch'
post '/info/lfs/objects', to: 'lfs_api#deprecated'
get '/info/lfs/objects/*oid', to: 'lfs_api#deprecated'
# GitLab LFS object storage
scope constraints: { oid: /[a-f0-9]{64}/ } do
get '/gitlab-lfs/objects/*oid', to: 'lfs_storage#download'
scope constraints: { size: /[0-9]+/ } do
put '/gitlab-lfs/objects/*oid/*size/authorize', to: 'lfs_storage#upload_authorize'
put '/gitlab-lfs/objects/*oid/*size', to: 'lfs_storage#upload_finalize'
end
end
end
# Allow /info/refs, /info/refs?service=git-upload-pack, and
# /info/refs?service=git-receive-pack, but nothing else.
#
git_http_handshake = lambda do |request|
request.query_string.blank? ||
request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/)
end
ref_redirect = redirect do |params, request|
path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
path << "?#{request.query_string}" unless request.query_string.blank?
path
end
get '/info/refs', constraints: git_http_handshake, to: ref_redirect
...@@ -3,7 +3,7 @@ require 'constraints/group_url_constrainer' ...@@ -3,7 +3,7 @@ require 'constraints/group_url_constrainer'
constraints(GroupUrlConstrainer.new) do constraints(GroupUrlConstrainer.new) do
scope(path: ':id', scope(path: ':id',
as: :group, as: :group,
constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }, constraints: { id: Gitlab::Regex.namespace_route_regex },
controller: :groups) do controller: :groups) do
get '/', action: :show get '/', action: :show
patch '/', action: :update patch '/', action: :update
...@@ -12,26 +12,26 @@ constraints(GroupUrlConstrainer.new) do ...@@ -12,26 +12,26 @@ constraints(GroupUrlConstrainer.new) do
end end
end end
scope constraints: { id: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ } do resources :groups, only: [:index, :new, :create]
resources :groups, except: [:show] do
member do
get :issues
get :merge_requests
get :projects
get :activity
end
scope module: :groups do scope(path: 'groups/:id', controller: :groups) do
resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do get :edit, as: :edit_group
post :resend_invite, on: :member get :issues, as: :issues_group
delete :leave, on: :collection get :merge_requests, as: :merge_requests_group
end get :projects, as: :projects_group
get :activity, as: :activity_group
resource :avatar, only: [:destroy] end
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create]
resources :labels, except: [:show], constraints: { id: /\d+/ } scope(path: 'groups/:group_id', module: :groups, as: :group) do
end resources :group_members, only: [:index, :create, :update, :destroy], concerns: :access_requestable do
post :resend_invite, on: :member
delete :leave, on: :collection
end end
get 'groups/:id' => 'groups#show', as: :group_canonical
resource :avatar, only: [:destroy]
resources :milestones, constraints: { id: /[^\/]+/ }, only: [:index, :show, :update, :new, :create]
resources :labels, except: [:show], constraints: { id: /\d+/ }
end end
# Must be last route in this file
get 'groups/:id' => 'groups#show', as: :group_canonical
...@@ -4,6 +4,7 @@ resource :profile, only: [:show, :update] do ...@@ -4,6 +4,7 @@ resource :profile, only: [:show, :update] do
get :applications, to: 'oauth/applications#index' get :applications, to: 'oauth/applications#index'
put :reset_private_token put :reset_private_token
put :reset_incoming_email_token
put :update_username put :update_username
end end
......
...@@ -18,152 +18,17 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: ...@@ -18,152 +18,17 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
get :autocomplete_sources get :autocomplete_sources
get :activity get :activity
get :refs get :refs
put :new_issue_address
end end
scope module: :projects do scope module: :projects do
scope constraints: { id: /.+\.git/, format: nil } do draw :git_http
# Git HTTP clients ('git clone' etc.)
get '/info/refs', to: 'git_http#info_refs'
post '/git-upload-pack', to: 'git_http#git_upload_pack'
post '/git-receive-pack', to: 'git_http#git_receive_pack'
# Git LFS API (metadata)
post '/info/lfs/objects/batch', to: 'lfs_api#batch'
post '/info/lfs/objects', to: 'lfs_api#deprecated'
get '/info/lfs/objects/*oid', to: 'lfs_api#deprecated'
# GitLab LFS object storage
scope constraints: { oid: /[a-f0-9]{64}/ } do
get '/gitlab-lfs/objects/*oid', to: 'lfs_storage#download'
scope constraints: { size: /[0-9]+/ } do
put '/gitlab-lfs/objects/*oid/*size/authorize', to: 'lfs_storage#upload_authorize'
put '/gitlab-lfs/objects/*oid/*size', to: 'lfs_storage#upload_finalize'
end
end
end
# Allow /info/refs, /info/refs?service=git-upload-pack, and
# /info/refs?service=git-receive-pack, but nothing else.
#
git_http_handshake = lambda do |request|
request.query_string.blank? ||
request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/)
end
ref_redirect = redirect do |params, request|
path = "#{params[:namespace_id]}/#{params[:project_id]}.git/info/refs"
path << "?#{request.query_string}" unless request.query_string.blank?
path
end
get '/info/refs', constraints: git_http_handshake, to: ref_redirect
# Blob routes:
get '/new/*id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob'
post '/create/*id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob'
get '/edit/*id', to: 'blob#edit', constraints: { id: /.+/ }, as: 'edit_blob'
put '/update/*id', to: 'blob#update', constraints: { id: /.+/ }, as: 'update_blob'
post '/preview/*id', to: 'blob#preview', constraints: { id: /.+/ }, as: 'preview_blob'
# #
# Templates # Templates
# #
get '/templates/:template_type/:key' => 'templates#show', as: :template get '/templates/:template_type/:key' => 'templates#show', as: :template
scope do
get(
'/blob/*id/diff',
to: 'blob#diff',
constraints: { id: /.+/, format: false },
as: :blob_diff
)
get(
'/blob/*id',
to: 'blob#show',
constraints: { id: /.+/, format: false },
as: :blob
)
delete(
'/blob/*id',
to: 'blob#destroy',
constraints: { id: /.+/, format: false }
)
put(
'/blob/*id',
to: 'blob#update',
constraints: { id: /.+/, format: false }
)
post(
'/blob/*id',
to: 'blob#create',
constraints: { id: /.+/, format: false }
)
end
scope do
get(
'/raw/*id',
to: 'raw#show',
constraints: { id: /.+/, format: /(html|js)/ },
as: :raw
)
end
scope do
get(
'/tree/*id',
to: 'tree#show',
constraints: { id: /.+/, format: /(html|js)/ },
as: :tree
)
end
scope do
get(
'/find_file/*id',
to: 'find_file#show',
constraints: { id: /.+/, format: /html/ },
as: :find_file
)
end
scope do
get(
'/files/*id',
to: 'find_file#list',
constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ },
as: :files
)
end
scope do
post(
'/create_dir/*id',
to: 'tree#create_dir',
constraints: { id: /.+/ },
as: 'create_dir'
)
end
scope do
get(
'/blame/*id',
to: 'blame#show',
constraints: { id: /.+/, format: /(html|js)/ },
as: :blame
)
end
scope do
get(
'/commits/*id',
to: 'commits#show',
constraints: { id: /.+/, format: false },
as: :commits
)
end
resource :avatar, only: [:show, :destroy] resource :avatar, only: [:show, :destroy]
resources :commit, only: [:show], constraints: { id: /\h{7,40}/ } do resources :commit, only: [:show], constraints: { id: /\h{7,40}/ } do
member do member do
...@@ -206,29 +71,6 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: ...@@ -206,29 +71,6 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
end end
end end
WIKI_SLUG_ID = { id: /\S+/ } unless defined? WIKI_SLUG_ID
scope do
# Order matters to give priority to these matches
get '/wikis/git_access', to: 'wikis#git_access'
get '/wikis/pages', to: 'wikis#pages', as: 'wiki_pages'
post '/wikis', to: 'wikis#create'
get '/wikis/*id/history', to: 'wikis#history', as: 'wiki_history', constraints: WIKI_SLUG_ID
get '/wikis/*id/edit', to: 'wikis#edit', as: 'wiki_edit', constraints: WIKI_SLUG_ID
get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID
delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID
put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID
post '/wikis/*id/preview_markdown', to: 'wikis#preview_markdown', constraints: WIKI_SLUG_ID, as: 'wiki_preview_markdown'
end
resource :repository, only: [:create] do
member do
get 'archive', constraints: { format: Gitlab::Regex.archive_formats_regex }
end
end
resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do resources :services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do
member do member do
get :test get :test
...@@ -245,23 +87,6 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: ...@@ -245,23 +87,6 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
resources :forks, only: [:index, :new, :create] resources :forks, only: [:index, :new, :create]
resource :import, only: [:new, :create, :show] resource :import, only: [:new, :create, :show]
resources :refs, only: [] do
collection do
get 'switch'
end
member do
# tree viewer logs
get 'logs_tree', constraints: { id: Gitlab::Regex.git_reference_regex }
# Directories with leading dots erroneously get rejected if git
# ref regex used in constraints. Regex verification now done in controller.
get 'logs_tree/*path' => 'refs#logs_tree', as: :logs_file, constraints: {
id: /.*/,
path: /.*/
}
end
end
resources :merge_requests, concerns: :awardable, constraints: { id: /\d+/ } do resources :merge_requests, concerns: :awardable, constraints: { id: /\d+/ } do
member do member do
get :commits get :commits
...@@ -467,6 +292,11 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: ...@@ -467,6 +292,11 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
end end
end end
end end
# Since both wiki and repository routing contains wildcard characters
# its preferable to keep it below all other project routes
draw :wiki
draw :repository
end end
end end
end end
# All routing related to repositoty browsing
resource :repository, only: [:create] do
member do
get 'archive', constraints: { format: Gitlab::Regex.archive_formats_regex }
end
end
resources :refs, only: [] do
collection do
get 'switch'
end
member do
# tree viewer logs
get 'logs_tree', constraints: { id: Gitlab::Regex.git_reference_regex }
# Directories with leading dots erroneously get rejected if git
# ref regex used in constraints. Regex verification now done in controller.
get 'logs_tree/*path' => 'refs#logs_tree', as: :logs_file, constraints: {
id: /.*/,
path: /.*/
}
end
end
get '/new/*id', to: 'blob#new', constraints: { id: /.+/ }, as: 'new_blob'
post '/create/*id', to: 'blob#create', constraints: { id: /.+/ }, as: 'create_blob'
get '/edit/*id', to: 'blob#edit', constraints: { id: /.+/ }, as: 'edit_blob'
put '/update/*id', to: 'blob#update', constraints: { id: /.+/ }, as: 'update_blob'
post '/preview/*id', to: 'blob#preview', constraints: { id: /.+/ }, as: 'preview_blob'
scope do
get(
'/blob/*id/diff',
to: 'blob#diff',
constraints: { id: /.+/, format: false },
as: :blob_diff
)
get(
'/blob/*id',
to: 'blob#show',
constraints: { id: /.+/, format: false },
as: :blob
)
delete(
'/blob/*id',
to: 'blob#destroy',
constraints: { id: /.+/, format: false }
)
put(
'/blob/*id',
to: 'blob#update',
constraints: { id: /.+/, format: false }
)
post(
'/blob/*id',
to: 'blob#create',
constraints: { id: /.+/, format: false }
)
get(
'/raw/*id',
to: 'raw#show',
constraints: { id: /.+/, format: /(html|js)/ },
as: :raw
)
get(
'/tree/*id',
to: 'tree#show',
constraints: { id: /.+/, format: /(html|js)/ },
as: :tree
)
get(
'/find_file/*id',
to: 'find_file#show',
constraints: { id: /.+/, format: /html/ },
as: :find_file
)
get(
'/files/*id',
to: 'find_file#list',
constraints: { id: /(?:[^.]|\.(?!json$))+/, format: /json/ },
as: :files
)
post(
'/create_dir/*id',
to: 'tree#create_dir',
constraints: { id: /.+/ },
as: 'create_dir'
)
get(
'/blame/*id',
to: 'blame#show',
constraints: { id: /.+/, format: /(html|js)/ },
as: :blame
)
# File/dir history
get(
'/commits/*id',
to: 'commits#show',
constraints: { id: /.+/, format: false },
as: :commits
)
end
...@@ -14,31 +14,32 @@ end ...@@ -14,31 +14,32 @@ end
constraints(UserUrlConstrainer.new) do constraints(UserUrlConstrainer.new) do
scope(path: ':username', scope(path: ':username',
as: :user, as: :user,
constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }, constraints: { username: Gitlab::Regex.namespace_route_regex },
controller: :users) do controller: :users) do
get '/', action: :show get '/', action: :show
end end
end end
scope(path: 'users/:username', scope(constraints: { username: Gitlab::Regex.namespace_route_regex }) do
as: :user, scope(path: 'users/:username',
constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ }, as: :user,
controller: :users) do controller: :users) do
get :calendar get :calendar
get :calendar_activities get :calendar_activities
get :groups get :groups
get :projects get :projects
get :contributed, as: :contributed_projects get :contributed, as: :contributed_projects
get :snippets get :snippets
get :exists get :exists
get '/', to: redirect('/%{username}') get '/', to: redirect('/%{username}')
end end
# Compatibility with old routing # Compatibility with old routing
# TODO (dzaporozhets): remove in 10.0 # TODO (dzaporozhets): remove in 10.0
get '/u/:username', to: redirect('/%{username}'), constraints: { username: /[a-zA-Z.0-9_\-]+(?<!\.atom)/ } get '/u/:username', to: redirect('/%{username}')
# TODO (dzaporozhets): remove in 9.0 # TODO (dzaporozhets): remove in 9.0
get '/u/:username/groups', to: redirect('/users/%{username}/groups'), constraints: { username: /[a-zA-Z.0-9_\-]+/ } get '/u/:username/groups', to: redirect('/users/%{username}/groups')
get '/u/:username/projects', to: redirect('/users/%{username}/projects'), constraints: { username: /[a-zA-Z.0-9_\-]+/ } get '/u/:username/projects', to: redirect('/users/%{username}/projects')
get '/u/:username/snippets', to: redirect('/users/%{username}/snippets'), constraints: { username: /[a-zA-Z.0-9_\-]+/ } get '/u/:username/snippets', to: redirect('/users/%{username}/snippets')
get '/u/:username/contributed', to: redirect('/users/%{username}/contributed'), constraints: { username: /[a-zA-Z.0-9_\-]+/ } get '/u/:username/contributed', to: redirect('/users/%{username}/contributed')
end
WIKI_SLUG_ID = { id: /\S+/ } unless defined? WIKI_SLUG_ID
scope do
# Order matters to give priority to these matches
get '/wikis/git_access', to: 'wikis#git_access'
get '/wikis/pages', to: 'wikis#pages', as: 'wiki_pages'
post '/wikis', to: 'wikis#create'
get '/wikis/*id/history', to: 'wikis#history', as: 'wiki_history', constraints: WIKI_SLUG_ID
get '/wikis/*id/edit', to: 'wikis#edit', as: 'wiki_edit', constraints: WIKI_SLUG_ID
get '/wikis/*id', to: 'wikis#show', as: 'wiki', constraints: WIKI_SLUG_ID
delete '/wikis/*id', to: 'wikis#destroy', constraints: WIKI_SLUG_ID
put '/wikis/*id', to: 'wikis#update', constraints: WIKI_SLUG_ID
post '/wikis/*id/preview_markdown', to: 'wikis#preview_markdown', constraints: WIKI_SLUG_ID, as: 'wiki_preview_markdown'
end
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
- [post_receive, 5] - [post_receive, 5]
- [merge, 5] - [merge, 5]
- [update_merge_requests, 3] - [update_merge_requests, 3]
- [process_commit, 2]
- [new_note, 2] - [new_note, 2]
- [build, 2] - [build, 2]
- [pipeline, 2] - [pipeline, 2]
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddIncomingEmailTokenToUsers < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def change
add_column :users, :incoming_email_token, :string
add_concurrent_index :users, :incoming_email_token
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddHousekeepingToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
# When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the
# migration requires downtime.
# DOWNTIME_REASON = ''
disable_ddl_transaction!
def up
add_column_with_default(:application_settings, :housekeeping_enabled, :boolean, default: true, allow_null: false)
add_column_with_default(:application_settings, :housekeeping_bitmaps_enabled, :boolean, default: true, allow_null: false)
add_column_with_default(:application_settings, :housekeeping_incremental_repack_period, :integer, default: 10, allow_null: false)
add_column_with_default(:application_settings, :housekeeping_full_repack_period, :integer, default: 50, allow_null: false)
add_column_with_default(:application_settings, :housekeeping_gc_period, :integer, default: 200, allow_null: false)
end
def down
remove_column(:application_settings, :housekeeping_enabled, :boolean, default: true, allow_null: false)
remove_column(:application_settings, :housekeeping_bitmaps_enabled, :boolean, default: true, allow_null: false)
remove_column(:application_settings, :housekeeping_incremental_repack_period, :integer, default: 10, allow_null: false)
remove_column(:application_settings, :housekeeping_full_repack_period, :integer, default: 50, allow_null: false)
remove_column(:application_settings, :housekeeping_gc_period, :integer, default: 200, allow_null: false)
end
end
...@@ -5,12 +5,12 @@ class RenameRepositoryStorageColumn < ActiveRecord::Migration ...@@ -5,12 +5,12 @@ class RenameRepositoryStorageColumn < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime. # Set this constant to true if this migration requires downtime.
DOWNTIME = false DOWNTIME = true
# When a migration requires downtime you **must** uncomment the following # When a migration requires downtime you **must** uncomment the following
# constant and define a short and easy to understand explanation as to why the # constant and define a short and easy to understand explanation as to why the
# migration requires downtime. # migration requires downtime.
# DOWNTIME_REASON = '' DOWNTIME_REASON = 'Renaming the application_settings.repository_storage column'
# When using the methods "add_concurrent_index" or "add_column_with_default" # When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an # you must disable the use of transactions as these methods can not run in an
......
class AddProjectImportDataProjectIndex < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def change
add_concurrent_index :project_import_data, :project_id
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161103171205) do ActiveRecord::Schema.define(version: 20161106185620) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -98,6 +98,11 @@ ActiveRecord::Schema.define(version: 20161103171205) do ...@@ -98,6 +98,11 @@ ActiveRecord::Schema.define(version: 20161103171205) do
t.text "help_page_text_html" t.text "help_page_text_html"
t.text "shared_runners_text_html" t.text "shared_runners_text_html"
t.text "after_sign_up_text_html" t.text "after_sign_up_text_html"
t.boolean "housekeeping_enabled", default: true, null: false
t.boolean "housekeeping_bitmaps_enabled", default: true, null: false
t.integer "housekeeping_incremental_repack_period", default: 10, null: false
t.integer "housekeeping_full_repack_period", default: 50, null: false
t.integer "housekeeping_gc_period", default: 200, null: false
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
...@@ -867,6 +872,8 @@ ActiveRecord::Schema.define(version: 20161103171205) do ...@@ -867,6 +872,8 @@ ActiveRecord::Schema.define(version: 20161103171205) do
t.string "encrypted_credentials_salt" t.string "encrypted_credentials_salt"
end end
add_index "project_import_data", ["project_id"], name: "index_project_import_data_on_project_id", using: :btree
create_table "projects", force: :cascade do |t| create_table "projects", force: :cascade do |t|
t.string "name" t.string "name"
t.string "path" t.string "path"
...@@ -1176,6 +1183,7 @@ ActiveRecord::Schema.define(version: 20161103171205) do ...@@ -1176,6 +1183,7 @@ ActiveRecord::Schema.define(version: 20161103171205) do
t.boolean "ldap_email", default: false, null: false t.boolean "ldap_email", default: false, null: false
t.boolean "external", default: false t.boolean "external", default: false
t.string "organization" t.string "organization"
t.string "incoming_email_token"
end end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
...@@ -1185,6 +1193,7 @@ ActiveRecord::Schema.define(version: 20161103171205) do ...@@ -1185,6 +1193,7 @@ ActiveRecord::Schema.define(version: 20161103171205) do
add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree add_index "users", ["current_sign_in_at"], name: "index_users_on_current_sign_in_at", using: :btree
add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree
add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"} add_index "users", ["email"], name: "index_users_on_email_trigram", using: :gin, opclasses: {"email"=>"gin_trgm_ops"}
add_index "users", ["incoming_email_token"], name: "index_users_on_incoming_email_token", using: :btree
add_index "users", ["name"], name: "index_users_on_name", using: :btree add_index "users", ["name"], name: "index_users_on_name", using: :btree
add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "users", ["name"], name: "index_users_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
......
...@@ -3,6 +3,14 @@ ...@@ -3,6 +3,14 @@
> [Introduced][ce-2371] in GitLab 8.4. > [Introduced][ce-2371] in GitLab 8.4.
--- ---
## Automatic housekeeping
GitLab automatically runs `git gc` and `git repack` on repositories
after Git pushes. If needed you can change how often this happens, or
to turn it off, go to **Admin area > Settings**
(`/admin/application_settings`).
## Manual housekeeping
The housekeeping function runs `git gc` ([man page][man]) on the current The housekeeping function runs `git gc` ([man page][man]) on the current
project Git repository. project Git repository.
......
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.
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