Commit 4dc12b03 authored by Yorick Peterse's avatar Yorick Peterse

Merge branch 'master' into 8-8-stable

parents e4280981 4ade5ff4
...@@ -276,7 +276,7 @@ Style/IdenticalConditionalBranches: ...@@ -276,7 +276,7 @@ Style/IdenticalConditionalBranches:
Enabled: false Enabled: false
# Checks the indentation of the first line of the right-hand-side of a # Checks the indentation of the first line of the right-hand-side of a
# multi-line assignment. # multi-line assignment.
Style/IndentAssignment: Style/IndentAssignment:
Enabled: false Enabled: false
...@@ -531,7 +531,7 @@ Style/SpaceAroundKeyword: ...@@ -531,7 +531,7 @@ Style/SpaceAroundKeyword:
# Use a single space around operators. # Use a single space around operators.
Style/SpaceAroundOperators: Style/SpaceAroundOperators:
Enabled: false Enabled: true
# Checks that the left block brace has or doesn't have space before it. # Checks that the left block brace has or doesn't have space before it.
Style/SpaceBeforeBlockBraces: Style/SpaceBeforeBlockBraces:
......
...@@ -5,6 +5,7 @@ v 8.8.0 (unreleased) ...@@ -5,6 +5,7 @@ v 8.8.0 (unreleased)
- Fix error when using link to uploads in global snippets - Fix error when using link to uploads in global snippets
- Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen) - Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen)
- Use a case-insensitive comparison in sanitizing URI schemes - Use a case-insensitive comparison in sanitizing URI schemes
- Toggle sign-up confirmation emails in application settings
- Project#open_branches has been cleaned up and no longer loads entire records into memory. - Project#open_branches has been cleaned up and no longer loads entire records into memory.
- Escape HTML in commit titles in system note messages - Escape HTML in commit titles in system note messages
- Improve multiple branch push performance by memoizing permission checking - Improve multiple branch push performance by memoizing permission checking
...@@ -15,6 +16,7 @@ v 8.8.0 (unreleased) ...@@ -15,6 +16,7 @@ v 8.8.0 (unreleased)
- Make build status canceled if any of the jobs was canceled and none failed - Make build status canceled if any of the jobs was canceled and none failed
- Upgrade Sidekiq to 4.1.2 - Upgrade Sidekiq to 4.1.2
- Added /health_check endpoint for checking service status - Added /health_check endpoint for checking service status
- Make 'upcoming' filter for milestones work better across projects
- Sanitize repo paths in new project error message - Sanitize repo paths in new project error message
- Bump mail_room to 0.7.0 to fix stuck IDLE connections - Bump mail_room to 0.7.0 to fix stuck IDLE connections
- Remove future dates from contribution calendar graph. - Remove future dates from contribution calendar graph.
...@@ -25,9 +27,11 @@ v 8.8.0 (unreleased) ...@@ -25,9 +27,11 @@ v 8.8.0 (unreleased)
- Update SVG sanitizer to conform to SVG 1.1 - Update SVG sanitizer to conform to SVG 1.1
- Speed up push emails with multiple recipients by only generating the email once - Speed up push emails with multiple recipients by only generating the email once
- Updated search UI - Updated search UI
- Added authentication service for Container Registry
- Display informative message when new milestone is created - Display informative message when new milestone is created
- Sanitize milestones and labels titles - Sanitize milestones and labels titles
- Support multi-line tag messages. !3833 (Calin Seciu) - Support multi-line tag messages. !3833 (Calin Seciu)
- Force users to reset their password after an admin changes it
- Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea) - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
- Added button to toggle whitespaces changes on diff view - Added button to toggle whitespaces changes on diff view
- Backport GitHub Enterprise import support from EE - Backport GitHub Enterprise import support from EE
...@@ -43,12 +47,19 @@ v 8.8.0 (unreleased) ...@@ -43,12 +47,19 @@ v 8.8.0 (unreleased)
- API support for the 'since' and 'until' operators on commit requests (Paco Guzman) - API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
- Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko) - Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
- Expire repository exists? and has_visible_content? caches after a push if necessary - Expire repository exists? and has_visible_content? caches after a push if necessary
- Fix unintentional filtering bug in issues sorted by milestone due (Takuya Noguchi) - Fix unintentional filtering bug in Issue/MR sorted by milestone due (Takuya Noguchi)
- Fix adding a todo for private group members (Ahmad Sherif) - Fix adding a todo for private group members (Ahmad Sherif)
- Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3 - Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3
- Total method execution timings are no longer tracked - Total method execution timings are no longer tracked
- Allow Admins to remove the Login with buttons for OAuth services and still be able to import !4034. (Andrei Gliga) - Allow Admins to remove the Login with buttons for OAuth services and still be able to import !4034. (Andrei Gliga)
- Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif) - Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif)
- Hide left sidebar on phone screens to give more space for content
- Redesign navigation for profile and group pages
- Add counter metrics for rails cache
- Import pull requests from GitHub where the source or target branches were removed
v 8.7.6
- Fix links on wiki pages for relative url setups. !4131 (Artem Sidorenko)
v 8.7.5 v 8.7.5
- Fix relative links in wiki pages. !4050 - Fix relative links in wiki pages. !4050
......
...@@ -36,6 +36,7 @@ gem 'omniauth-shibboleth', '~> 1.2.0' ...@@ -36,6 +36,7 @@ gem 'omniauth-shibboleth', '~> 1.2.0'
gem 'omniauth-twitter', '~> 1.2.0' gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd', '~> 2.2.0' gem 'omniauth_crowd', '~> 2.2.0'
gem 'rack-oauth2', '~> 1.2.1' gem 'rack-oauth2', '~> 1.2.1'
gem 'jwt'
# Spam and anti-bot protection # Spam and anti-bot protection
gem 'recaptcha', require: 'recaptcha/rails' gem 'recaptcha', require: 'recaptcha/rails'
...@@ -224,6 +225,7 @@ gem 'request_store', '~> 1.3.0' ...@@ -224,6 +225,7 @@ gem 'request_store', '~> 1.3.0'
gem 'select2-rails', '~> 3.5.9' gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1' gem 'virtus', '~> 1.0.1'
gem 'net-ssh', '~> 3.0.1' gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0'
# Sentry integration # Sentry integration
gem 'sentry-raven', '~> 0.15' gem 'sentry-raven', '~> 0.15'
...@@ -323,7 +325,6 @@ gem "mail_room", "~> 0.7" ...@@ -323,7 +325,6 @@ gem "mail_room", "~> 0.7"
gem 'email_reply_parser', '~> 0.5.8' gem 'email_reply_parser', '~> 0.5.8'
## CI ## CI
gem 'activerecord-deprecated_finders', '~> 1.0.3'
gem 'activerecord-session_store', '~> 0.1.0' gem 'activerecord-session_store', '~> 0.1.0'
gem "nested_form", '~> 0.3.2' gem "nested_form", '~> 0.3.2'
......
...@@ -33,7 +33,6 @@ GEM ...@@ -33,7 +33,6 @@ GEM
activemodel (= 4.2.6) activemodel (= 4.2.6)
activesupport (= 4.2.6) activesupport (= 4.2.6)
arel (~> 6.0) arel (~> 6.0)
activerecord-deprecated_finders (1.0.4)
activerecord-session_store (0.1.2) activerecord-session_store (0.1.2)
actionpack (>= 4.0.0, < 5) actionpack (>= 4.0.0, < 5)
activerecord (>= 4.0.0, < 5) activerecord (>= 4.0.0, < 5)
...@@ -71,6 +70,7 @@ GEM ...@@ -71,6 +70,7 @@ GEM
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
babosa (1.0.2) babosa (1.0.2)
base32 (0.3.2)
bcrypt (3.1.10) bcrypt (3.1.10)
benchmark-ips (2.3.0) benchmark-ips (2.3.0)
better_errors (1.0.1) better_errors (1.0.1)
...@@ -883,7 +883,6 @@ PLATFORMS ...@@ -883,7 +883,6 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
RedCloth (~> 4.2.9) RedCloth (~> 4.2.9)
ace-rails-ap (~> 4.0.2) ace-rails-ap (~> 4.0.2)
activerecord-deprecated_finders (~> 1.0.3)
activerecord-session_store (~> 0.1.0) activerecord-session_store (~> 0.1.0)
acts-as-taggable-on (~> 3.4) acts-as-taggable-on (~> 3.4)
addressable (~> 2.3.8) addressable (~> 2.3.8)
...@@ -895,6 +894,7 @@ DEPENDENCIES ...@@ -895,6 +894,7 @@ DEPENDENCIES
attr_encrypted (~> 1.3.4) attr_encrypted (~> 1.3.4)
awesome_print (~> 1.2.0) awesome_print (~> 1.2.0)
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0)
benchmark-ips benchmark-ips
better_errors (~> 1.0.1) better_errors (~> 1.0.1)
binding_of_caller (~> 0.7.2) binding_of_caller (~> 0.7.2)
...@@ -956,6 +956,7 @@ DEPENDENCIES ...@@ -956,6 +956,7 @@ DEPENDENCIES
jquery-rails (~> 4.1.0) jquery-rails (~> 4.1.0)
jquery-turbolinks (~> 2.1.0) jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
jwt
kaminari (~> 0.16.3) kaminari (~> 0.16.3)
letter_opener_web (~> 1.3.0) letter_opener_web (~> 1.3.0)
licensee (~> 8.0.0) licensee (~> 8.0.0)
......
...@@ -285,6 +285,7 @@ class @Notes ...@@ -285,6 +285,7 @@ class @Notes
form.addClass "js-main-target-form" form.addClass "js-main-target-form"
form.find("#note_line_code").remove() form.find("#note_line_code").remove()
form.find("#note_type").remove()
### ###
General note form setup. General note form setup.
...@@ -472,6 +473,7 @@ class @Notes ...@@ -472,6 +473,7 @@ class @Notes
setupDiscussionNoteForm: (dataHolder, form) => setupDiscussionNoteForm: (dataHolder, form) =>
# setup note target # setup note target
form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}" form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}"
form.find("#note_type").val dataHolder.data("noteType")
form.find("#line_type").val dataHolder.data("lineType") form.find("#line_type").val dataHolder.data("lineType")
form.find("#note_commit_id").val dataHolder.data("commitId") form.find("#note_commit_id").val dataHolder.data("commitId")
form.find("#note_line_code").val dataHolder.data("lineCode") form.find("#note_line_code").val dataHolder.data("lineCode")
......
...@@ -289,7 +289,7 @@ table { ...@@ -289,7 +289,7 @@ table {
text-shadow: none; text-shadow: none;
@media (min-width: $screen-sm-min) { @media (min-width: $screen-sm-min) {
margin-top: 11px; margin-top: 8px;
} }
} }
......
...@@ -9,8 +9,7 @@ ...@@ -9,8 +9,7 @@
@mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) { @mixin gitlab-theme($color-light, $color, $color-darker, $color-dark) {
.page-with-sidebar { .page-with-sidebar {
.header-logo { .header-logo {
background-color: $color; background: $color-darker;
border-color: $color;
a { a {
color: $color-light; color: $color-light;
...@@ -21,7 +20,7 @@ ...@@ -21,7 +20,7 @@
} }
&:hover { &:hover {
background-color: $color-darker; background-color: $color-dark;
a { a {
color: #fff; color: #fff;
...@@ -91,8 +90,8 @@ ...@@ -91,8 +90,8 @@
} }
$theme-blue: #2980b9; $theme-blue: #2980b9;
$theme-charcoal: #333c47; $theme-charcoal: #3d454d;
$theme-graphite: #888; $theme-graphite: #666;
$theme-gray: #373737; $theme-gray: #373737;
$theme-green: #019875; $theme-green: #019875;
$theme-violet: #548; $theme-violet: #548;
...@@ -103,11 +102,11 @@ body { ...@@ -103,11 +102,11 @@ body {
} }
&.ui_charcoal { &.ui_charcoal {
@include gitlab-theme(#c5d0de, $theme-charcoal, #2b333d, #24272d); @include gitlab-theme(#d6d7d9, #485157, $theme-charcoal, #353b41);
} }
&.ui_graphite { &.ui_graphite {
@include gitlab-theme(#ccc, $theme-graphite, #777, #666); @include gitlab-theme(#ccc, #777, $theme-graphite, #555);
} }
&.ui_gray { &.ui_gray {
......
...@@ -6,12 +6,12 @@ header { ...@@ -6,12 +6,12 @@ header {
transition-duration: .3s; transition-duration: .3s;
&.navbar-empty { &.navbar-empty {
height: 58px; height: $header-height;
background: #fff; background: #fff;
border-bottom: 1px solid $btn-gray-hover; border-bottom: 1px solid $btn-gray-hover;
.center-logo { .center-logo {
margin: 11px 0; margin: 8px 0;
text-align: center; text-align: center;
#tanuki-logo, img { #tanuki-logo, img {
......
...@@ -209,6 +209,15 @@ ...@@ -209,6 +209,15 @@
float: right; float: right;
padding: 7px 0 0; padding: 7px 0 0;
@media (max-width: $screen-xs-min) {
float: none;
padding: 0 9px;
.dropdown-new {
width: 100%;
}
}
i { i {
color: $layout-link-gray; color: $layout-link-gray;
} }
...@@ -225,6 +234,10 @@ ...@@ -225,6 +234,10 @@
.dropdown { .dropdown {
margin-left: 7px; margin-left: 7px;
@media (max-width: $screen-xs-min) {
margin-left: 0;
}
} }
} }
...@@ -260,4 +273,10 @@ ...@@ -260,4 +273,10 @@
.page-with-layout-nav { .page-with-layout-nav {
margin-top: 50px; margin-top: 50px;
&.controls-dropdown-visible {
@media (max-width: $screen-xs-min) {
margin-top: 96px;
}
}
} }
...@@ -312,7 +312,7 @@ ...@@ -312,7 +312,7 @@
} }
.nav-sidebar li a { .nav-sidebar li a {
width: 230px; width: $sidebar_width;
&.back-link { &.back-link {
i { i {
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Layout * Layout
*/ */
$sidebar_collapsed_width: 62px; $sidebar_collapsed_width: 62px;
$sidebar_width: 230px; $sidebar_width: 220px;
$gutter_collapsed_width: 62px; $gutter_collapsed_width: 62px;
$gutter_width: 290px; $gutter_width: 290px;
$gutter_inner_width: 258px; $gutter_inner_width: 258px;
......
...@@ -125,7 +125,7 @@ ...@@ -125,7 +125,7 @@
.right-sidebar { .right-sidebar {
position: fixed; position: fixed;
top: 58px; top: $header-height;
bottom: 0; bottom: 0;
right: 0; right: 0;
z-index: 10; z-index: 10;
......
...@@ -226,8 +226,7 @@ ul.notes { ...@@ -226,8 +226,7 @@ ul.notes {
} }
} }
.note-action-button, .note-action-button {
.discussion-action-button {
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 10px;
line-height: 24px; line-height: 24px;
......
...@@ -106,6 +106,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -106,6 +106,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:email_author_in_body, :email_author_in_body,
:repository_checks_enabled, :repository_checks_enabled,
:metrics_packet_size, :metrics_packet_size,
:send_user_confirmation_email,
restricted_visibility_levels: [], restricted_visibility_levels: [],
import_sources: [], import_sources: [],
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: []
......
...@@ -119,6 +119,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -119,6 +119,7 @@ class Admin::UsersController < Admin::ApplicationController
user_params_with_pass.merge!( user_params_with_pass.merge!(
password: params[:user][:password], password: params[:user][:password],
password_confirmation: params[:user][:password_confirmation], password_confirmation: params[:user][:password_confirmation],
password_expires_at: Time.now
) )
end end
......
...@@ -176,7 +176,7 @@ class ApplicationController < ActionController::Base ...@@ -176,7 +176,7 @@ class ApplicationController < ActionController::Base
end end
def check_password_expiration def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user? if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
redirect_to new_profile_password_path and return redirect_to new_profile_password_path and return
end end
end end
......
...@@ -122,7 +122,7 @@ module CreatesCommit ...@@ -122,7 +122,7 @@ module CreatesCommit
# Merge request from fork to this project # Merge request from fork to this project
@mr_source_project = @tree_edit_project @mr_source_project = @tree_edit_project
@mr_target_project = @project @mr_target_project = @project
@mr_target_branch ||= @ref @mr_target_branch ||= @ref
end end
end end
end end
class JwtController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
before_action :authenticate_project_or_user
SERVICES = {
Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService,
}
def auth
service = SERVICES[params[:service]]
return head :not_found unless service
result = service.new(@project, @user, auth_params).execute
render json: result, status: result[:http_status]
end
private
def authenticate_project_or_user
authenticate_with_http_basic do |login, password|
# if it's possible we first try to authenticate project with login and password
@project = authenticate_project(login, password)
return if @project
@user = authenticate_user(login, password)
return if @user
render_403
end
end
def auth_params
params.permit(:service, :scope, :offline_token, :account, :client_id)
end
def authenticate_project(login, password)
if login == 'gitlab_ci_token'
Project.find_by(builds_enabled: true, runners_token: password)
end
end
def authenticate_user(login, password)
# TODO: this is a copy and paste from grack_auth,
# it should be refactored in the future
user = Gitlab::Auth.new.find(login, password)
# If the user authenticated successfully, we reset the auth failure count
# from Rack::Attack for that IP. A client may attempt to authenticate
# with a username and blank password first, and only after it receives
# a 401 error does it present a password. Resetting the count prevents
# false positives from occurring.
#
# Otherwise, we let Rack::Attack know there was a failed authentication
# attempt from this IP. This information is stored in the Rails cache
# (Redis) and will be used by the Rack::Attack middleware to decide
# whether to block requests from this IP.
config = Gitlab.config.rack_attack.git_basic_auth
if config.enabled
if user
# A successful login will reset the auth failure count from this IP
Rack::Attack::Allow2Ban.reset(request.ip, config)
else
banned = Rack::Attack::Allow2Ban.filter(request.ip, config) do
# Unless the IP is whitelisted, return true so that Allow2Ban
# increments the counter (stored in Rails.cache) for the IP
if config.ip_whitelist.include?(request.ip)
false
else
true
end
end
if banned
Rails.logger.info "IP #{request.ip} failed to login " \
"as #{login} but has been temporarily banned from Git auth"
return
end
end
end
user
end
end
...@@ -17,12 +17,12 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -17,12 +17,12 @@ class Projects::CommitController < Projects::ApplicationController
def show def show
apply_diff_view_cookie! apply_diff_view_cookie!
@line_notes = commit.notes.inline @grouped_diff_notes = commit.notes.grouped_diff_notes
@note = @project.build_commit_note(commit) @note = @project.build_commit_note(commit)
@notes = commit.notes.not_inline.fresh @notes = commit.notes.non_diff_notes.fresh
@noteable = @commit @noteable = @commit
@comments_allowed = @reply_allowed = true @comments_target = {
@comments_target = {
noteable_type: 'Commit', noteable_type: 'Commit',
commit_id: @commit.id commit_id: @commit.id
} }
...@@ -67,10 +67,10 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -67,10 +67,10 @@ class Projects::CommitController < Projects::ApplicationController
create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title} has been successfully reverted.", create_commit(Commits::RevertService, success_notice: "The #{@commit.change_type_title} has been successfully reverted.",
success_path: successful_change_path, failure_path: failed_change_path) success_path: successful_change_path, failure_path: failed_change_path)
end end
def cherry_pick def cherry_pick
assign_change_commit_vars(@commit.cherry_pick_branch_name) assign_change_commit_vars(@commit.cherry_pick_branch_name)
return render_404 if @target_branch.blank? return render_404 if @target_branch.blank?
create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title} has been successfully cherry-picked.", create_commit(Commits::CherryPickService, success_notice: "The #{@commit.change_type_title} has been successfully cherry-picked.",
......
...@@ -22,7 +22,8 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -22,7 +22,8 @@ class Projects::CompareController < Projects::ApplicationController
@base_commit = @project.merge_base_commit(@base_ref, @head_ref) @base_commit = @project.merge_base_commit(@base_ref, @head_ref)
@diffs = compare.diffs(diff_options) @diffs = compare.diffs(diff_options)
@diff_refs = [@base_commit, @commit] @diff_refs = [@base_commit, @commit]
@line_notes = [] @diff_notes_disabled = true
@grouped_diff_notes = {}
end end
end end
......
...@@ -73,12 +73,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -73,12 +73,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
# but we need it for the "View file @ ..." link by deleted files # but we need it for the "View file @ ..." link by deleted files
@base_commit ||= @merge_request.first_commit.parent || @merge_request.first_commit @base_commit ||= @merge_request.first_commit.parent || @merge_request.first_commit
@comments_allowed = @reply_allowed = true
@comments_target = { @comments_target = {
noteable_type: 'MergeRequest', noteable_type: 'MergeRequest',
noteable_id: @merge_request.id noteable_id: @merge_request.id
} }
@line_notes = @merge_request.notes.where("line_code is not null")
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
respond_to do |format| respond_to do |format|
format.html format.html
...@@ -117,6 +117,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -117,6 +117,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commit = @merge_request.last_commit @commit = @merge_request.last_commit
@base_commit = @merge_request.diff_base_commit @base_commit = @merge_request.diff_base_commit
@diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare @diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
@diff_notes_disabled = true
@ci_commit = @merge_request.ci_commit @ci_commit = @merge_request.ci_commit
@statuses = @ci_commit.statuses if @ci_commit @statuses = @ci_commit.statuses if @ci_commit
...@@ -300,7 +301,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -300,7 +301,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request) @note = @project.notes.new(noteable: @merge_request)
@notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh
@discussions = Note.discussions_from_notes(@notes) @discussions = @notes.discussions
@noteable = @merge_request @noteable = @merge_request
# Get commits from repository # Get commits from repository
......
...@@ -96,7 +96,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -96,7 +96,7 @@ class Projects::NotesController < Projects::ApplicationController
end end
def note_to_discussion_html(note) def note_to_discussion_html(note)
return unless note.for_diff_line? return unless note.diff_note?
if params[:view] == 'parallel' if params[:view] == 'parallel'
template = "projects/notes/_diff_notes_with_reply_parallel" template = "projects/notes/_diff_notes_with_reply_parallel"
...@@ -120,7 +120,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -120,7 +120,7 @@ class Projects::NotesController < Projects::ApplicationController
end end
def note_to_discussion_with_diff_html(note) def note_to_discussion_with_diff_html(note)
return unless note.for_diff_line? return unless note.diff_note?
render_to_string( render_to_string(
"projects/notes/_discussion", "projects/notes/_discussion",
...@@ -158,7 +158,7 @@ class Projects::NotesController < Projects::ApplicationController ...@@ -158,7 +158,7 @@ class Projects::NotesController < Projects::ApplicationController
def note_params def note_params
params.require(:note).permit( params.require(:note).permit(
:note, :noteable, :noteable_id, :noteable_type, :project_id, :note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code, :commit_id :attachment, :line_code, :commit_id, :type
) )
end end
......
...@@ -235,7 +235,8 @@ class ProjectsController < Projects::ApplicationController ...@@ -235,7 +235,8 @@ class ProjectsController < Projects::ApplicationController
def project_params def project_params
params.require(:project).permit( params.require(:project).permit(
:name, :path, :description, :issues_tracker, :tag_list, :runners_token, :name, :path, :description, :issues_tracker, :tag_list, :runners_token,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch, :issues_enabled, :merge_requests_enabled, :snippets_enabled, :container_registry_enabled,
:issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds, :public_builds,
......
...@@ -37,8 +37,8 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -37,8 +37,8 @@ class RegistrationsController < Devise::RegistrationsController
super super
end end
def after_sign_up_path_for(_resource) def after_sign_up_path_for(user)
users_almost_there_path user.confirmed_at.present? ? dashboard_projects_path : users_almost_there_path
end end
def after_inactive_sign_up_path_for(_resource) def after_inactive_sign_up_path_for(_resource)
......
...@@ -252,8 +252,8 @@ class IssuableFinder ...@@ -252,8 +252,8 @@ class IssuableFinder
if filter_by_no_milestone? if filter_by_no_milestone?
items = items.where(milestone_id: [-1, nil]) items = items.where(milestone_id: [-1, nil])
elsif filter_by_upcoming_milestone? elsif filter_by_upcoming_milestone?
upcoming = Milestone.where(project_id: projects).upcoming upcoming_ids = Milestone.upcoming_ids_by_projects(projects)
items = items.joins(:milestone).where(milestones: { title: upcoming.try(:title) }) items = items.joins(:milestone).where(milestone_id: upcoming_ids)
else else
items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] }) items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] })
......
...@@ -10,7 +10,7 @@ class NotesFinder ...@@ -10,7 +10,7 @@ class NotesFinder
notes = notes =
case target_type case target_type
when "commit" when "commit"
project.notes.for_commit_id(target_id).not_inline project.notes.for_commit_id(target_id).non_diff_notes
when "issue" when "issue"
project.issues.find(target_id).notes.nonawards.inc_author project.issues.find(target_id).notes.nonawards.inc_author
when "merge_request" when "merge_request"
......
...@@ -55,22 +55,18 @@ module DiffHelper ...@@ -55,22 +55,18 @@ module DiffHelper
end end
end end
def line_comments def organize_comments(left, right)
@line_comments ||= @line_notes.select(&:active?).sort_by(&:created_at).group_by(&:line_code) notes_left = notes_right = nil
end
def organize_comments(type_left, type_right, line_code_left, line_code_right)
comments_left = comments_right = nil
unless type_left.nil? && type_right == 'new' unless left[:type].nil? && right[:type] == 'new'
comments_left = line_comments[line_code_left] notes_left = @grouped_diff_notes[left[:line_code]]
end end
unless type_left.nil? && type_right.nil? unless left[:type].nil? && right[:type].nil?
comments_right = line_comments[line_code_right] notes_right = @grouped_diff_notes[right[:line_code]]
end end
[comments_left, comments_right] [notes_left, notes_right]
end end
def inline_diff_btn def inline_diff_btn
...@@ -96,8 +92,8 @@ module DiffHelper ...@@ -96,8 +92,8 @@ module DiffHelper
].join(' ').html_safe ].join(' ').html_safe
end end
def commit_for_diff(diff) def commit_for_diff(diff_file)
if diff.deleted_file if diff_file.deleted_file
@base_commit || @commit.parent || @commit @base_commit || @commit.parent || @commit
else else
@commit @commit
......
...@@ -3,7 +3,7 @@ module EventsHelper ...@@ -3,7 +3,7 @@ module EventsHelper
author = event.author author = event.author
if author if author
link_to author.name, user_path(author.username), title: h(author.name) link_to author.name, user_path(author.username), title: author.name
else else
event.author_name event.author_name
end end
...@@ -57,11 +57,7 @@ module EventsHelper ...@@ -57,11 +57,7 @@ module EventsHelper
words << event.ref_name words << event.ref_name
words << "at" words << "at"
elsif event.commented? elsif event.commented?
if event.note_commit? words << event.note_target_reference
words << event.note_short_commit_id
else
words << "##{truncate event.note_target_iid}"
end
words << "at" words << "at"
elsif event.milestone? elsif event.milestone?
words << "##{event.target_iid}" if event.target_iid words << "##{event.target_iid}" if event.target_iid
...@@ -84,21 +80,12 @@ module EventsHelper ...@@ -84,21 +80,12 @@ module EventsHelper
elsif event.merge_request? elsif event.merge_request?
namespace_project_merge_request_url(event.project.namespace, namespace_project_merge_request_url(event.project.namespace,
event.project, event.merge_request) event.project, event.merge_request)
elsif event.note? && event.note_commit? elsif event.note? && event.commit_note?
namespace_project_commit_url(event.project.namespace, event.project, namespace_project_commit_url(event.project.namespace, event.project,
event.note_target) event.note_target)
elsif event.note? elsif event.note?
if event.note_target if event.note_target
if event.note_commit? event_note_target_path(event)
namespace_project_commit_path(event.project.namespace, event.project,
event.note_commit_id,
anchor: dom_id(event.target))
elsif event.note_project_snippet?
namespace_project_snippet_path(event.project.namespace,
event.project, event.note_target)
else
event_note_target_path(event)
end
end end
elsif event.push? elsif event.push?
push_event_feed_url(event) push_event_feed_url(event)
...@@ -134,42 +121,30 @@ module EventsHelper ...@@ -134,42 +121,30 @@ module EventsHelper
end end
def event_note_target_path(event) def event_note_target_path(event)
if event.note? && event.note_commit? if event.note? && event.commit_note?
namespace_project_commit_path(event.project.namespace, event.project, namespace_project_commit_path(event.project.namespace,
event.note_target) event.project,
event.note_target,
anchor: dom_id(event.target))
elsif event.project_snippet_note?
namespace_project_snippet_path(event.project.namespace,
event.project,
event.note_target,
anchor: dom_id(event.target))
else else
polymorphic_path([event.project.namespace.becomes(Namespace), polymorphic_path([event.project.namespace.becomes(Namespace),
event.project, event.note_target], event.project, event.note_target],
anchor: dom_id(event.target)) anchor: dom_id(event.target))
end end
end end
def event_note_title_html(event) def event_note_title_html(event)
if event.note_target if event.note_target
if event.note_commit? link_to(event_note_target_path(event), title: event.target_title, class: 'has-tooltip') do
link_to( "#{event.note_target_type} #{event.note_target_reference}"
namespace_project_commit_path(event.project.namespace, event.project,
event.note_commit_id,
anchor: dom_id(event.target), title: h(event.target_title)),
class: "commit_short_id"
) do
"#{event.note_target_type} #{event.note_short_commit_id}"
end
elsif event.note_project_snippet?
link_to(namespace_project_snippet_path(event.project.namespace,
event.project,
event.note_target), title: h(event.project.name)) do
"#{event.note_target_type} #{truncate event.note_target.to_reference}"
end
else
link_to event_note_target_path(event) do
"#{event.note_target_type} #{truncate event.note_target.to_reference}"
end
end end
else else
content_tag :strong do content_tag(:strong, '(deleted)')
"(deleted)"
end
end end
end end
......
...@@ -43,4 +43,12 @@ module NavHelper ...@@ -43,4 +43,12 @@ module NavHelper
class_name += " with-horizontal-nav" if defined?(nav) && nav class_name += " with-horizontal-nav" if defined?(nav) && nav
class_name class_name
end end
def layout_nav_class
"page-with-layout-nav" if defined?(nav) && nav
end
def layout_dropdown_class
"controls-dropdown-visible" if current_user
end
end end
module NotesHelper module NotesHelper
# Helps to distinguish e.g. commit notes in mr notes list # Helps to distinguish e.g. commit notes in mr notes list
def note_for_main_target?(note) def note_for_main_target?(note)
(@noteable.class.name == note.noteable_type && !note.for_diff_line?) @noteable.class.name == note.noteable_type && !note.diff_note?
end end
def note_target_fields(note) def note_target_fields(note)
...@@ -15,16 +15,6 @@ module NotesHelper ...@@ -15,16 +15,6 @@ module NotesHelper
note.editable? && can?(current_user, :admin_note, note) note.editable? && can?(current_user, :admin_note, note)
end end
def link_to_commit_diff_line_note(note)
if note.for_commit_diff_line?
link_to(
"#{note.diff_file_name}:L#{note.diff_new_line}",
namespace_project_commit_path(@project.namespace, @project,
note.noteable, anchor: note.line_code)
)
end
end
def noteable_json(noteable) def noteable_json(noteable)
{ {
id: noteable.id, id: noteable.id,
...@@ -35,7 +25,7 @@ module NotesHelper ...@@ -35,7 +25,7 @@ module NotesHelper
end end
def link_to_new_diff_note(line_code, line_type = nil) def link_to_new_diff_note(line_code, line_type = nil)
discussion_id = Note.build_discussion_id( discussion_id = LegacyDiffNote.build_discussion_id(
@comments_target[:noteable_type], @comments_target[:noteable_type],
@comments_target[:noteable_id] || @comments_target[:commit_id], @comments_target[:noteable_id] || @comments_target[:commit_id],
line_code line_code
...@@ -45,9 +35,10 @@ module NotesHelper ...@@ -45,9 +35,10 @@ module NotesHelper
noteable_type: @comments_target[:noteable_type], noteable_type: @comments_target[:noteable_type],
noteable_id: @comments_target[:noteable_id], noteable_id: @comments_target[:noteable_id],
commit_id: @comments_target[:commit_id], commit_id: @comments_target[:commit_id],
line_type: line_type,
line_code: line_code, line_code: line_code,
discussion_id: discussion_id, note_type: LegacyDiffNote.name,
line_type: line_type discussion_id: discussion_id
} }
button_tag(class: 'btn add-diff-note js-add-diff-note-button', button_tag(class: 'btn add-diff-note js-add-diff-note-button',
...@@ -57,18 +48,24 @@ module NotesHelper ...@@ -57,18 +48,24 @@ module NotesHelper
end end
end end
def link_to_reply_diff(note, line_type = nil) def link_to_reply_discussion(note, line_type = nil)
return unless current_user return unless current_user
data = { data = {
noteable_type: note.noteable_type, noteable_type: note.noteable_type,
noteable_id: note.noteable_id, noteable_id: note.noteable_id,
commit_id: note.commit_id, commit_id: note.commit_id,
line_code: note.line_code,
discussion_id: note.discussion_id, discussion_id: note.discussion_id,
line_type: line_type line_type: line_type
} }
if note.diff_note?
data.merge!(
line_code: note.line_code,
note_type: LegacyDiffNote.name
)
end
button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button', button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button',
data: data, title: 'Add a reply' data: data, title: 'Add a reply'
end end
......
...@@ -59,7 +59,7 @@ module SearchHelper ...@@ -59,7 +59,7 @@ module SearchHelper
# Autocomplete results for the current project, if it's defined # Autocomplete results for the current project, if it's defined
def project_autocomplete def project_autocomplete
if @project && @project.repository.exists? && @project.repository.root_ref if @project && @project.repository.exists? && @project.repository.root_ref
ref = @ref || @project.repository.root_ref ref = @ref || @project.repository.root_ref
[ [
{ category: "Current Project", label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) }, { category: "Current Project", label: "Files", url: namespace_project_tree_path(@project.namespace, @project, ref) },
......
...@@ -18,7 +18,7 @@ module SelectsHelper ...@@ -18,7 +18,7 @@ module SelectsHelper
first_user: first_user, first_user: first_user,
current_user: opts[:current_user] || false, current_user: opts[:current_user] || false,
"push-code-to-protected-branches" => opts[:push_code_to_protected_branches], "push-code-to-protected-branches" => opts[:push_code_to_protected_branches],
author_id: opts[:author_id] || '' author_id: opts[:author_id] || ''
} }
} }
......
...@@ -61,6 +61,7 @@ class Ability ...@@ -61,6 +61,7 @@ class Ability
:read_merge_request, :read_merge_request,
:read_note, :read_note,
:read_commit_status, :read_commit_status,
:read_container_image,
:download_code :download_code
] ]
...@@ -203,6 +204,7 @@ class Ability ...@@ -203,6 +204,7 @@ class Ability
:admin_label, :admin_label,
:read_commit_status, :read_commit_status,
:read_build, :read_build,
:read_container_image,
] ]
end end
...@@ -216,7 +218,9 @@ class Ability ...@@ -216,7 +218,9 @@ class Ability
:update_build, :update_build,
:create_merge_request, :create_merge_request,
:create_wiki, :create_wiki,
:push_code :push_code,
:create_container_image,
:update_container_image,
] ]
end end
...@@ -242,7 +246,8 @@ class Ability ...@@ -242,7 +246,8 @@ class Ability
:admin_wiki, :admin_wiki,
:admin_project, :admin_project,
:admin_commit_status, :admin_commit_status,
:admin_build :admin_build,
:admin_container_image,
] ]
end end
...@@ -287,6 +292,10 @@ class Ability ...@@ -287,6 +292,10 @@ class Ability
rules += named_abilities('build') rules += named_abilities('build')
end end
unless project.container_registry_enabled
rules += named_abilities('container_image')
end
rules rules
end end
......
...@@ -120,7 +120,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -120,7 +120,8 @@ class ApplicationSetting < ActiveRecord::Base
recaptcha_enabled: false, recaptcha_enabled: false,
akismet_enabled: false, akismet_enabled: false,
repository_checks_enabled: true, repository_checks_enabled: true,
disabled_oauth_sign_in_sources: [] disabled_oauth_sign_in_sources: [],
send_user_confirmation_email: false
) )
end end
......
...@@ -80,7 +80,7 @@ class Event < ActiveRecord::Base ...@@ -80,7 +80,7 @@ class Event < ActiveRecord::Base
end end
def target_title def target_title
target.title if target && target.respond_to?(:title) target.try(:title)
end end
def created? def created?
...@@ -266,28 +266,20 @@ class Event < ActiveRecord::Base ...@@ -266,28 +266,20 @@ class Event < ActiveRecord::Base
branch? && project.default_branch != branch_name branch? && project.default_branch != branch_name
end end
def note_commit_id
target.commit_id
end
def target_iid def target_iid
target.respond_to?(:iid) ? target.iid : target_id target.respond_to?(:iid) ? target.iid : target_id
end end
def note_short_commit_id def commit_note?
Commit.truncate_sha(note_commit_id) target.for_commit?
end
def note_commit?
target.noteable_type == "Commit"
end end
def issue_note? def issue_note?
note? && target && target.noteable_type == "Issue" note? && target && target.for_issue?
end end
def note_project_snippet? def project_snippet_note?
target.noteable_type == "Snippet" target.for_snippet?
end end
def note_target def note_target
...@@ -295,19 +287,22 @@ class Event < ActiveRecord::Base ...@@ -295,19 +287,22 @@ class Event < ActiveRecord::Base
end end
def note_target_id def note_target_id
if note_commit? if commit_note?
target.commit_id target.commit_id
else else
target.noteable_id.to_s target.noteable_id.to_s
end end
end end
def note_target_iid def note_target_reference
if note_target.respond_to?(:iid) return unless note_target
note_target.iid
# Commit#to_reference returns the full SHA, but we want the short one here
if commit_note?
note_target.short_id
else else
note_target_id note_target.to_reference
end.to_s end
end end
def note_target_type def note_target_type
......
class LegacyDiffNote < Note
serialize :st_diff
validates :line_code, presence: true, line_code: true
before_create :set_diff
class << self
def build_discussion_id(noteable_type, noteable_id, line_code, active = true)
[super(noteable_type, noteable_id), line_code, active].join("-")
end
end
def diff_note?
true
end
def legacy_diff_note?
true
end
def discussion_id
@discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code, active?)
end
def diff_file_hash
line_code.split('_')[0] if line_code
end
def diff_old_line
line_code.split('_')[1].to_i if line_code
end
def diff_new_line
line_code.split('_')[2].to_i if line_code
end
def diff
@diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map)
end
def diff_file_path
diff.new_path.presence || diff.old_path
end
def diff_lines
@diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
end
def diff_line
@diff_line ||= diff_lines.find { |line| generate_line_code(line) == self.line_code }
end
def diff_line_text
diff_line.try(:text)
end
def diff_line_type
diff_line.try(:type)
end
def highlighted_diff_lines
Gitlab::Diff::Highlight.new(diff_lines).highlight
end
def truncated_diff_lines
max_number_of_lines = 16
prev_match_line = nil
prev_lines = []
highlighted_diff_lines.each do |line|
if line.type == "match"
prev_lines.clear
prev_match_line = line
else
prev_lines << line
break if generate_line_code(line) == self.line_code
prev_lines.shift if prev_lines.length >= max_number_of_lines
end
end
prev_lines
end
# Check if this note is part of an "active" discussion
#
# This will always return true for anything except MergeRequest noteables,
# which have special logic.
#
# If the note's current diff cannot be matched in the MergeRequest's current
# diff, it's considered inactive.
def active?
return @active if defined?(@active)
return true if for_commit?
return true unless self.diff
return false unless noteable
noteable_diff = find_noteable_diff
if noteable_diff
parsed_lines = Gitlab::Diff::Parser.new.parse(noteable_diff.diff.each_line)
@active = parsed_lines.any? { |line_obj| line_obj.text == diff_line_text }
else
@active = false
end
@active
end
private
def find_diff
return nil unless noteable
return @diff if defined?(@diff)
@diff = noteable.diffs(Commit.max_diff_options).find do |d|
d.new_path && Digest::SHA1.hexdigest(d.new_path) == diff_file_hash
end
end
def set_diff
# First lets find notes with same diff
# before iterating over all mr diffs
diff = diff_for_line_code unless for_merge_request?
diff ||= find_diff
self.st_diff = diff.to_hash if diff
end
def diff_for_line_code
attributes = {
noteable_type: noteable_type,
line_code: line_code
}
if for_commit?
attributes[:commit_id] = commit_id
else
attributes[:noteable_id] = noteable_id
end
self.class.where(attributes).last.try(:diff)
end
def generate_line_code(line)
Gitlab::Diff::LineCode.generate(diff_file_path, line.new_pos, line.old_pos)
end
# Find the diff on noteable that matches our own
def find_noteable_diff
diffs = noteable.diffs(Commit.max_diff_options)
diffs.find { |d| d.new_path == self.diff.new_path }
end
end
...@@ -26,6 +26,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -26,6 +26,10 @@ class MergeRequest < ActiveRecord::Base
# when creating new merge request # when creating new merge request
attr_accessor :can_be_created, :compare_commits, :compare attr_accessor :can_be_created, :compare_commits, :compare
# Temporary fields to store target_sha, and base_sha to
# compare when importing pull requests from GitHub
attr_accessor :base_target_sha, :head_source_sha
state_machine :state, initial: :opened do state_machine :state, initial: :opened do
event :close do event :close do
transition [:reopened, :opened] => :closed transition [:reopened, :opened] => :closed
...@@ -490,10 +494,14 @@ class MergeRequest < ActiveRecord::Base ...@@ -490,10 +494,14 @@ class MergeRequest < ActiveRecord::Base
end end
def target_sha def target_sha
@target_sha ||= target_project.repository.commit(target_branch).try(:sha) return @base_target_sha if defined?(@base_target_sha)
target_project.repository.commit(target_branch).try(:sha)
end end
def source_sha def source_sha
return @head_source_sha if defined?(@head_source_sha)
last_commit.try(:sha) || source_tip.try(:sha) last_commit.try(:sha) || source_tip.try(:sha)
end end
......
...@@ -6,7 +6,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -6,7 +6,7 @@ class MergeRequestDiff < ActiveRecord::Base
belongs_to :merge_request belongs_to :merge_request
delegate :target_branch, :source_branch, to: :merge_request, prefix: nil delegate :head_source_sha, :target_branch, :source_branch, to: :merge_request, prefix: nil
state_machine :state, initial: :empty do state_machine :state, initial: :empty do
state :collected state :collected
...@@ -38,8 +38,8 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -38,8 +38,8 @@ class MergeRequestDiff < ActiveRecord::Base
@diffs_no_whitespace ||= begin @diffs_no_whitespace ||= begin
compare = Gitlab::Git::Compare.new( compare = Gitlab::Git::Compare.new(
self.repository.raw_repository, self.repository.raw_repository,
self.target_branch, self.base,
self.source_sha, self.head,
) )
compare.diffs(options) compare.diffs(options)
end end
...@@ -144,7 +144,7 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -144,7 +144,7 @@ class MergeRequestDiff < ActiveRecord::Base
self.st_diffs = new_diffs self.st_diffs = new_diffs
self.base_commit_sha = self.repository.merge_base(self.source_sha, self.target_branch) self.base_commit_sha = self.repository.merge_base(self.head, self.base)
self.save self.save
end end
...@@ -160,10 +160,24 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -160,10 +160,24 @@ class MergeRequestDiff < ActiveRecord::Base
end end
def source_sha def source_sha
return head_source_sha if head_source_sha.present?
source_commit = merge_request.source_project.commit(source_branch) source_commit = merge_request.source_project.commit(source_branch)
source_commit.try(:sha) source_commit.try(:sha)
end end
def target_sha
merge_request.target_sha
end
def base
self.target_sha || self.target_branch
end
def head
self.source_sha
end
def compare def compare
@compare ||= @compare ||=
begin begin
...@@ -172,8 +186,8 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -172,8 +186,8 @@ class MergeRequestDiff < ActiveRecord::Base
Gitlab::Git::Compare.new( Gitlab::Git::Compare.new(
self.repository.raw_repository, self.repository.raw_repository,
self.target_branch, self.base,
self.source_sha self.head
) )
end end
end end
......
...@@ -67,8 +67,18 @@ class Milestone < ActiveRecord::Base ...@@ -67,8 +67,18 @@ class Milestone < ActiveRecord::Base
@link_reference_pattern ||= super("milestones", /(?<milestone>\d+)/) @link_reference_pattern ||= super("milestones", /(?<milestone>\d+)/)
end end
def self.upcoming def self.upcoming_ids_by_projects(projects)
self.where('due_date > ?', Time.now).reorder(due_date: :asc).first rel = unscoped.of_projects(projects).active.where('due_date > ?', Time.now)
if Gitlab::Database.postgresql?
rel.order(:project_id, :due_date).select('DISTINCT ON (project_id) id')
else
rel.
group(:project_id).
having('due_date = MIN(due_date)').
pluck(:id, :project_id, :due_date).
map(&:first)
end
end end
def to_reference(from_project = nil) def to_reference(from_project = nil)
......
require 'carrierwave/orm/activerecord'
class Note < ActiveRecord::Base class Note < ActiveRecord::Base
extend ActiveModel::Naming
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
include Participable include Participable
include Mentionable include Mentionable
...@@ -20,14 +19,13 @@ class Note < ActiveRecord::Base ...@@ -20,14 +19,13 @@ class Note < ActiveRecord::Base
delegate :gfm_reference, :local_reference, to: :noteable delegate :gfm_reference, :local_reference, to: :noteable
delegate :name, to: :project, prefix: true delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true delegate :name, :email, to: :author, prefix: true
delegate :title, to: :noteable, allow_nil: true
before_validation :set_award! before_validation :set_award!
before_validation :clear_blank_line_code!
validates :note, :project, presence: true validates :note, :project, presence: true
validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award } validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award }
validates :note, inclusion: { in: Emoji.emojis_names }, if: ->(n) { n.is_award } validates :note, inclusion: { in: Emoji.emojis_names }, if: ->(n) { n.is_award }
validates :line_code, line_code: true, allow_blank: true
# Attachments are deprecated and are handled by Markdown uploader # Attachments are deprecated and are handled by Markdown uploader
validates :attachment, file_size: { maximum: :max_attachment_size } validates :attachment, file_size: { maximum: :max_attachment_size }
...@@ -41,8 +39,6 @@ class Note < ActiveRecord::Base ...@@ -41,8 +39,6 @@ class Note < ActiveRecord::Base
scope :awards, ->{ where(is_award: true) } scope :awards, ->{ where(is_award: true) }
scope :nonawards, ->{ where(is_award: false) } scope :nonawards, ->{ where(is_award: false) }
scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) } scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) }
scope :inline, ->{ where("line_code IS NOT NULL") }
scope :not_inline, ->{ where(line_code: nil) }
scope :system, ->{ where(system: true) } scope :system, ->{ where(system: true) }
scope :user, ->{ where(system: false) } scope :user, ->{ where(system: false) }
scope :common, ->{ where(noteable_type: ["", nil]) } scope :common, ->{ where(noteable_type: ["", nil]) }
...@@ -50,38 +46,31 @@ class Note < ActiveRecord::Base ...@@ -50,38 +46,31 @@ class Note < ActiveRecord::Base
scope :inc_author_project, ->{ includes(:project, :author) } scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) } scope :inc_author, ->{ includes(:author) }
scope :legacy_diff_notes, ->{ where(type: 'LegacyDiffNote') }
scope :non_diff_notes, ->{ where(type: ['Note', nil]) }
scope :with_associations, -> do scope :with_associations, -> do
includes(:author, :noteable, :updated_by, includes(:author, :noteable, :updated_by,
project: [:project_members, { group: [:group_members] }]) project: [:project_members, { group: [:group_members] }])
end end
serialize :st_diff before_validation :clear_blank_line_code!
before_create :set_diff, if: ->(n) { n.line_code.present? }
class << self class << self
def discussions_from_notes(notes) def model_name
discussion_ids = [] ActiveModel::Name.new(self, nil, 'note')
discussions = [] end
notes.each do |note| def build_discussion_id(noteable_type, noteable_id)
next if discussion_ids.include?(note.discussion_id) [:discussion, noteable_type.try(:underscore), noteable_id].join("-")
end
# don't group notes for the main target
if !note.for_diff_line? && note.for_merge_request?
discussions << [note]
else
discussions << notes.select do |other_note|
note.discussion_id == other_note.discussion_id
end
discussion_ids << note.discussion_id
end
end
discussions def discussions
all.group_by(&:discussion_id).values
end end
def build_discussion_id(type, id, line_code) def grouped_diff_notes
[:discussion, type.try(:underscore), id, line_code].join("-").to_sym legacy_diff_notes.select(&:active?).sort_by(&:created_at).group_by(&:line_code)
end end
# Searches for notes matching the given query. # Searches for notes matching the given query.
...@@ -116,167 +105,39 @@ class Note < ActiveRecord::Base ...@@ -116,167 +105,39 @@ class Note < ActiveRecord::Base
system && SystemNoteService.cross_reference?(note) system && SystemNoteService.cross_reference?(note)
end end
def max_attachment_size def diff_note?
current_application_settings.max_attachment_size.megabytes.to_i false
end
def find_diff
return nil unless noteable
return @diff if defined?(@diff)
# Don't use ||= because nil is a valid value for @diff
@diff = noteable.diffs(Commit.max_diff_options).find do |d|
Digest::SHA1.hexdigest(d.new_path) == diff_file_index if d.new_path
end
end
def hook_attrs
attributes
end end
def set_diff def legacy_diff_note?
# First lets find notes with same diff false
# before iterating over all mr diffs
diff = diff_for_line_code unless for_merge_request?
diff ||= find_diff
self.st_diff = diff.to_hash if diff
end end
def diff
@diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map)
end
def diff_for_line_code
Note.where(noteable_id: noteable_id, noteable_type: noteable_type, line_code: line_code).last.try(:diff)
end
# Check if this note is part of an "active" discussion
#
# This will always return true for anything except MergeRequest noteables,
# which have special logic.
#
# If the note's current diff cannot be matched in the MergeRequest's current
# diff, it's considered inactive.
def active? def active?
return true unless self.diff true
return false unless noteable
return @active if defined?(@active)
noteable_diff = find_noteable_diff
if noteable_diff
parsed_lines = Gitlab::Diff::Parser.new.parse(noteable_diff.diff.each_line)
@active = parsed_lines.any? { |line_obj| line_obj.text == diff_line }
else
@active = false
end
@active
end
def diff_file_index
line_code.split('_')[0] if line_code
end
def diff_file_name
diff.new_path if diff
end end
def file_path def discussion_id
if diff.new_path.present? @discussion_id ||=
diff.new_path if for_merge_request?
elsif diff.old_path.present? [:discussion, :note, id].join("-")
diff.old_path
end
end
def diff_old_line
line_code.split('_')[1].to_i if line_code
end
def diff_new_line
line_code.split('_')[2].to_i if line_code
end
def generate_line_code(line)
Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
end
def diff_line
return @diff_line if @diff_line
if diff
diff_lines.each do |line|
if generate_line_code(line) == self.line_code
@diff_line = line.text
end
end
end
@diff_line
end
def diff_line_type
return @diff_line_type if @diff_line_type
if diff
diff_lines.each do |line|
if generate_line_code(line) == self.line_code
@diff_line_type = line.type
end
end
end
@diff_line_type
end
def truncated_diff_lines
max_number_of_lines = 16
prev_match_line = nil
prev_lines = []
highlighted_diff_lines.each do |line|
if line.type == "match"
prev_lines.clear
prev_match_line = line
else else
prev_lines << line self.class.build_discussion_id(noteable_type, noteable_id || commit_id)
break if generate_line_code(line) == self.line_code
prev_lines.shift if prev_lines.length >= max_number_of_lines
end end
end
prev_lines
end
def diff_lines
@diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
end end
def highlighted_diff_lines def max_attachment_size
Gitlab::Diff::Highlight.new(diff_lines).highlight current_application_settings.max_attachment_size.megabytes.to_i
end end
def discussion_id def hook_attrs
@discussion_id ||= Note.build_discussion_id(noteable_type, noteable_id || commit_id, line_code) attributes
end end
def for_commit? def for_commit?
noteable_type == "Commit" noteable_type == "Commit"
end end
def for_commit_diff_line?
for_commit? && for_diff_line?
end
def for_diff_line?
line_code.present?
end
def for_issue? def for_issue?
noteable_type == "Issue" noteable_type == "Issue"
end end
...@@ -285,10 +146,6 @@ class Note < ActiveRecord::Base ...@@ -285,10 +146,6 @@ class Note < ActiveRecord::Base
noteable_type == "MergeRequest" noteable_type == "MergeRequest"
end end
def for_merge_request_diff_line?
for_merge_request? && for_diff_line?
end
def for_snippet? def for_snippet?
noteable_type == "Snippet" noteable_type == "Snippet"
end end
...@@ -361,14 +218,8 @@ class Note < ActiveRecord::Base ...@@ -361,14 +218,8 @@ class Note < ActiveRecord::Base
self.line_code = nil if self.line_code.blank? self.line_code = nil if self.line_code.blank?
end end
# Find the diff on noteable that matches our own
def find_noteable_diff
diffs = noteable.diffs(Commit.max_diff_options)
diffs.find { |d| d.new_path == self.diff.new_path }
end
def awards_supported? def awards_supported?
(for_issue? || for_merge_request?) && !for_diff_line? (for_issue? || for_merge_request?) && !diff_note?
end end
def contains_emoji_only? def contains_emoji_only?
......
...@@ -22,6 +22,7 @@ class Project < ActiveRecord::Base ...@@ -22,6 +22,7 @@ class Project < ActiveRecord::Base
default_value_for :builds_enabled, gitlab_config_features.builds default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :container_registry_enabled, gitlab_config_features.container_registry
default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled } default_value_for(:shared_runners_enabled) { current_application_settings.shared_runners_enabled }
# set last_activity_at to the same as created_at # set last_activity_at to the same as created_at
...@@ -49,6 +50,8 @@ class Project < ActiveRecord::Base ...@@ -49,6 +50,8 @@ class Project < ActiveRecord::Base
attr_accessor :new_default_branch attr_accessor :new_default_branch
attr_accessor :old_path_with_namespace attr_accessor :old_path_with_namespace
alias_attribute :title, :name
# Relations # Relations
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id' belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id'
...@@ -327,6 +330,12 @@ class Project < ActiveRecord::Base ...@@ -327,6 +330,12 @@ class Project < ActiveRecord::Base
@repository ||= Repository.new(path_with_namespace, self) @repository ||= Repository.new(path_with_namespace, self)
end end
def container_registry_url
if container_registry_enabled? && Gitlab.config.registry.enabled
"#{Gitlab.config.registry.host_with_port}/#{path_with_namespace}"
end
end
def commit(id = 'HEAD') def commit(id = 'HEAD')
repository.commit(id) repository.commit(id)
end end
......
...@@ -25,7 +25,7 @@ class ExternalWikiService < Service ...@@ -25,7 +25,7 @@ class ExternalWikiService < Service
def execute(_data) def execute(_data)
@response = HTTParty.get(properties['external_wiki_url'], verify: true) rescue nil @response = HTTParty.get(properties['external_wiki_url'], verify: true) rescue nil
if @response !=200 if @response != 200
nil nil
end end
end end
......
...@@ -40,7 +40,7 @@ class ProjectWiki ...@@ -40,7 +40,7 @@ class ProjectWiki
end end
def wiki_base_path def wiki_base_path
["/", @project.path_with_namespace, "/wikis"].join('') [Gitlab.config.gitlab.relative_url_root, "/", @project.path_with_namespace, "/wikis"].join('')
end end
# Returns the Gollum::Wiki object. # Returns the Gollum::Wiki object.
...@@ -113,7 +113,7 @@ class ProjectWiki ...@@ -113,7 +113,7 @@ class ProjectWiki
end end
def page_title_and_dir(title) def page_title_and_dir(title)
title_array = title.split("/") title_array = title.split("/")
title = title_array.pop title = title_array.pop
[title, title_array.join("/")] [title, title_array.join("/")]
end end
......
...@@ -195,6 +195,10 @@ class Repository ...@@ -195,6 +195,10 @@ class Repository
cache.fetch(:branch_names) { branches.map(&:name) } cache.fetch(:branch_names) { branches.map(&:name) }
end end
def branch_exists?(branch_name)
branch_names.include?(branch_name)
end
def tag_names def tag_names
cache.fetch(:tag_names) { raw_repository.tag_names } cache.fetch(:tag_names) { raw_repository.tag_names }
end end
...@@ -795,7 +799,7 @@ class Repository ...@@ -795,7 +799,7 @@ class Repository
def check_revert_content(commit, base_branch) def check_revert_content(commit, base_branch)
source_sha = find_branch(base_branch).target source_sha = find_branch(base_branch).target
args = [commit.id, source_sha] args = [commit.id, source_sha]
args << { mainline: 1 } if commit.merge_commit? args << { mainline: 1 } if commit.merge_commit?
revert_index = rugged.revert_commit(*args) revert_index = rugged.revert_commit(*args)
return false if revert_index.conflicts? return false if revert_index.conflicts?
...@@ -809,7 +813,7 @@ class Repository ...@@ -809,7 +813,7 @@ class Repository
def check_cherry_pick_content(commit, base_branch) def check_cherry_pick_content(commit, base_branch)
source_sha = find_branch(base_branch).target source_sha = find_branch(base_branch).target
args = [commit.id, source_sha] args = [commit.id, source_sha]
args << 1 if commit.merge_commit? args << 1 if commit.merge_commit?
cherry_pick_index = rugged.cherrypick_commit(*args) cherry_pick_index = rugged.cherrypick_commit(*args)
return false if cherry_pick_index.conflicts? return false if cherry_pick_index.conflicts?
......
...@@ -112,6 +112,7 @@ class User < ActiveRecord::Base ...@@ -112,6 +112,7 @@ class User < ActiveRecord::Base
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
before_create :check_confirmation_email
after_create :post_create_hook after_create :post_create_hook
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
...@@ -307,6 +308,10 @@ class User < ActiveRecord::Base ...@@ -307,6 +308,10 @@ class User < ActiveRecord::Base
@reset_token @reset_token
end end
def check_confirmation_email
skip_confirmation! unless current_application_settings.send_user_confirmation_email
end
def recently_sent_password_reset? def recently_sent_password_reset?
reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago
end end
......
module Auth
class ContainerRegistryAuthenticationService < BaseService
AUDIENCE = 'container_registry'
def execute
return error('not found', 404) unless registry.enabled
if params[:offline_token]
return error('forbidden', 403) unless current_user
else
return error('forbidden', 403) unless scope
end
{ token: authorized_token(scope).encoded }
end
private
def authorized_token(*accesses)
token = JSONWebToken::RSAToken.new(registry.key)
token.issuer = registry.issuer
token.audience = params[:service]
token.subject = current_user.try(:username)
token[:access] = accesses.compact
token
end
def scope
return unless params[:scope]
@scope ||= process_scope(params[:scope])
end
def process_scope(scope)
type, name, actions = scope.split(':', 3)
actions = actions.split(',')
return unless type == 'repository'
process_repository_access(type, name, actions)
end
def process_repository_access(type, name, actions)
requested_project = Project.find_with_namespace(name)
return unless requested_project
actions = actions.select do |action|
can_access?(requested_project, action)
end
{ type: type, name: name, actions: actions } if actions.present?
end
def can_access?(requested_project, requested_action)
return false unless requested_project.container_registry_enabled?
case requested_action
when 'pull'
requested_project == project || can?(current_user, :read_container_image, requested_project)
when 'push'
requested_project == project || can?(current_user, :create_container_image, requested_project)
else
false
end
end
def registry
Gitlab.config.registry
end
end
end
...@@ -66,7 +66,7 @@ module MergeRequests ...@@ -66,7 +66,7 @@ module MergeRequests
commits = merge_request.compare_commits commits = merge_request.compare_commits
if commits && commits.count == 1 if commits && commits.count == 1
commit = commits.first commit = commits.first
merge_request.title = commit.title merge_request.title = commit.title
merge_request.description ||= commit.description.try(:strip) merge_request.description ||= commit.description.try(:strip)
elsif iid && (issue = merge_request.target_project.get_issue(iid)) && !issue.try(:confidential?) elsif iid && (issue = merge_request.target_project.get_issue(iid)) && !issue.try(:confidential?)
case issue case issue
......
...@@ -85,7 +85,7 @@ class SystemHooksService ...@@ -85,7 +85,7 @@ class SystemHooksService
path_with_namespace: model.path_with_namespace, path_with_namespace: model.path_with_namespace,
project_id: model.id, project_id: model.id,
owner_name: owner.name, owner_name: owner.name,
owner_email: owner.respond_to?(:email) ? owner.email : "", owner_email: owner.respond_to?(:email) ? owner.email : "",
project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
} }
end end
......
...@@ -103,6 +103,12 @@ ...@@ -103,6 +103,12 @@
= f.label :signup_enabled do = f.label :signup_enabled do
= f.check_box :signup_enabled = f.check_box :signup_enabled
Sign-up enabled Sign-up enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :send_user_confirmation_email do
= f.check_box :send_user_confirmation_email
Send confirmation email on sign-up
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
= event_action_name(event) = event_action_name(event)
- if event.target - if event.target
%strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target] %strong= link_to event.target.reference_link_text, [event.project.namespace.becomes(Namespace), event.project, event.target], title: event.target_title
= event_preposition(event) = event_preposition(event)
......
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" } .page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
= render "layouts/broadcast"
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class } .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
.header-logo .header-logo
%a#logo %a#logo
...@@ -26,7 +25,8 @@ ...@@ -26,7 +25,8 @@
.layout-nav .layout-nav
.container-fluid .container-fluid
= render "layouts/nav/#{nav}" = render "layouts/nav/#{nav}"
.content-wrapper{ class: ('page-with-layout-nav' if defined?(nav) && nav) } .content-wrapper{ class: "#{layout_nav_class} #{layout_dropdown_class}" }
= render "layouts/broadcast"
= render "layouts/flash" = render "layouts/flash"
= yield :flash_message = yield :flash_message
%div{ class: (container_class unless @no_container) } %div{ class: (container_class unless @no_container) }
......
- if current_user - if current_user
- if access = @group.users.find_by(id: current_user.id) - if access = @group.users.find_by(id: current_user.id)
.controls .controls
%span.dropdown.group-settings-dropdown .dropdown.group-settings-dropdown
%a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'} %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
= icon('cog') = icon('cog')
= icon('caret-down') = icon('caret-down')
......
- if @note.diff_file_name - if @note.legacy_diff_note?
%p.details %p.details
New comment on diff for New comment on diff for
= link_to @note.diff_file_name, @target_url = link_to @note.diff_file_path, @target_url
\: \:
= render 'note_message' = render 'note_message'
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
= link_text = link_text
- else - else
= link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text }
- if @comments_allowed && can?(current_user, :create_note, @project) - if !@diff_notes_disabled && can?(current_user, :create_note, @project)
= link_to_new_diff_note(line_code) = link_to_new_diff_note(line_code)
%td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } } %td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } }
- link_text = type == "old" ? "&nbsp;".html_safe : line.new_pos - link_text = type == "old" ? "&nbsp;".html_safe : line.new_pos
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
- else - else
%td.old_line.diff-line-num{id: left[:line_code], class: "#{left[:type]} #{'empty-cell' if !left[:number]}"} %td.old_line.diff-line-num{id: left[:line_code], class: "#{left[:type]} #{'empty-cell' if !left[:number]}"}
= link_to raw(left[:number]), "##{left[:line_code]}", id: left[:line_code] = link_to raw(left[:number]), "##{left[:line_code]}", id: left[:line_code]
- if @comments_allowed && can?(current_user, :create_note, @project) - if !@diff_notes_disabled && can?(current_user, :create_note, @project)
= link_to_new_diff_note(left[:line_code], 'old') = link_to_new_diff_note(left[:line_code], 'old')
%td.line_content{class: "parallel noteable_line #{left[:type]} #{left[:line_code]} #{'empty-cell' if left[:text].empty?}", data: { line_code: left[:line_code] }}= diff_line_content(left[:text]) %td.line_content{class: "parallel noteable_line #{left[:type]} #{left[:line_code]} #{'empty-cell' if left[:text].empty?}", data: { line_code: left[:line_code] }}= diff_line_content(left[:text])
...@@ -29,14 +29,14 @@ ...@@ -29,14 +29,14 @@
%td.new_line.diff-line-num{id: new_line_code, class: "#{new_line_class} #{'empty-cell' if !right[:number]}", data: { linenumber: right[:number] }} %td.new_line.diff-line-num{id: new_line_code, class: "#{new_line_class} #{'empty-cell' if !right[:number]}", data: { linenumber: right[:number] }}
= link_to raw(right[:number]), "##{new_line_code}", id: new_line_code = link_to raw(right[:number]), "##{new_line_code}", id: new_line_code
- if @comments_allowed && can?(current_user, :create_note, @project) - if !@diff_notes_disabled && can?(current_user, :create_note, @project)
= link_to_new_diff_note(right[:line_code], 'new') = link_to_new_diff_note(new_line_code, 'new')
%td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code} #{'empty-cell' if right[:text].empty?}", data: { line_code: new_line_code }}= diff_line_content(right[:text]) %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code} #{'empty-cell' if right[:text].empty?}", data: { line_code: new_line_code }}= diff_line_content(right[:text])
- if @reply_allowed - unless @diff_notes_disabled
- comments_left, comments_right = organize_comments(left[:type], right[:type], left[:line_code], right[:line_code]) - notes_left, notes_right = organize_comments(left, right)
- if comments_left.present? || comments_right.present? - if notes_left.present? || notes_right.present?
= render "projects/notes/diff_notes_with_reply_parallel", notes_left: comments_left, notes_right: comments_right = render "projects/notes/diff_notes_with_reply_parallel", notes_left: notes_left, notes_right: notes_right
- if diff_file.diff.diff.blank? && diff_file.mode_changed? - if diff_file.diff.diff.blank? && diff_file.mode_changed?
.file-mode-changed .file-mode-changed
......
...@@ -6,16 +6,15 @@ ...@@ -6,16 +6,15 @@
%table.text-file.code.js-syntax-highlight{ class: too_big ? 'hide' : '' } %table.text-file.code.js-syntax-highlight{ class: too_big ? 'hide' : '' }
- last_line = 0 - last_line = 0
- raw_diff_lines = diff_file.diff_lines.to_a
- diff_file.highlighted_diff_lines.each_with_index do |line, index| - diff_file.highlighted_diff_lines.each_with_index do |line, index|
- line_code = generate_line_code(diff_file.file_path, line) - line_code = generate_line_code(diff_file.file_path, line)
- last_line = line.new_pos - last_line = line.new_pos
= render "projects/diffs/line", {line: line, diff_file: diff_file, line_code: line_code} = render "projects/diffs/line", {line: line, diff_file: diff_file, line_code: line_code}
- if @reply_allowed - unless @diff_notes_disabled
- comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at) - diff_notes = @grouped_diff_notes[line_code]
- unless comments.empty? - if diff_notes
= render "projects/notes/diff_notes_with_reply", notes: comments, line: raw_diff_lines[index].text = render "projects/notes/diff_notes_with_reply", notes: diff_notes
- if last_line > 0 - if last_line > 0
= render "projects/diffs/match_line", { line: "", = render "projects/diffs/match_line", { line: "",
......
...@@ -84,6 +84,16 @@ ...@@ -84,6 +84,16 @@
%br %br
%span.descr Share code pastes with others out of git repository %span.descr Share code pastes with others out of git repository
- if Gitlab.config.registry.enabled
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :container_registry_enabled do
= f.check_box :container_registry_enabled
%strong Container Registry
%br
%span.descr Enable Container Registry for this repository
= render 'builds_settings', f: f = render 'builds_settings', f: f
%fieldset.features %fieldset.features
......
- note = notes.first # example note - note = notes.first
-# Check if line want not changed since comment was left %tr.notes_holder
- if !defined?(line) || line == note.diff_line %td.notes_line{ colspan: 2 }
%tr.notes_holder %td.notes_content
%td.notes_line{ colspan: 2 } %ul.notes{ data: { discussion_id: note.discussion_id } }
%td.notes_content = render partial: "projects/notes/note", collection: notes, as: :note
%ul.notes{ data: { discussion_id: note.discussion_id } } .discussion-reply-holder
= render notes = link_to_reply_discussion(note)
.discussion-reply-holder
= link_to_reply_diff(note)
- note1 = notes_left.present? ? notes_left.first : nil - note_left = notes_left.present? ? notes_left.first : nil
- note2 = notes_right.present? ? notes_right.first : nil - note_right = notes_right.present? ? notes_right.first : nil
%tr.notes_holder %tr.notes_holder
- if note1 - if note_left
%td.notes_line.old %td.notes_line.old
%td.notes_content.parallel.old %td.notes_content.parallel.old
%ul.notes{ data: { discussion_id: note1.discussion_id } } %ul.notes{ data: { discussion_id: note_left.discussion_id } }
= render notes_left = render partial: "projects/notes/note", collection: notes_left, as: :note
.discussion-reply-holder .discussion-reply-holder
= link_to_reply_diff(note1, 'old') = link_to_reply_discussion(note_left, 'old')
- else - else
%td.notes_line.old= "" %td.notes_line.old= ""
%td.notes_content.parallel.old= "" %td.notes_content.parallel.old= ""
- if note2 - if note_right
%td.notes_line.new %td.notes_line.new
%td.notes_content.parallel.new %td.notes_content.parallel.new
%ul.notes{ data: { discussion_id: note2.discussion_id } } %ul.notes{ data: { discussion_id: note_right.discussion_id } }
= render notes_right = render partial: "projects/notes/note", collection: notes_right, as: :note
.discussion-reply-holder .discussion-reply-holder
= link_to_reply_diff(note2, 'new') = link_to_reply_discussion(note_right, 'new')
- else - else
%td.notes_line.new= "" %td.notes_line.new= ""
%td.notes_content.parallel.new= "" %td.notes_content.parallel.new= ""
- note = discussion_notes.first - note = discussion_notes.first
- expanded = !note.diff_note? || note.active?
%li.note.note-discussion.timeline-entry %li.note.note-discussion.timeline-entry
.timeline-entry-inner .timeline-entry-inner
.timeline-icon .timeline-icon
= link_to user_path(note.author) do = link_to user_path(note.author) do
= image_tag avatar_icon(note.author_email), class: "avatar s40" = image_tag avatar_icon(note.author), class: "avatar s40"
.timeline-content .timeline-content
- if note.for_merge_request? .discussion.js-toggle-container{ class: note.discussion_id }
- (active_notes, outdated_notes) = discussion_notes.partition(&:active?) .discussion-header
= render "projects/notes/discussions/active", discussion_notes: active_notes if active_notes.length > 0 = link_to_member(@project, note.author, avatar: false)
= render "projects/notes/discussions/outdated", discussion_notes: outdated_notes if outdated_notes.length > 0
- else .inline.discussion-headline-light
= render "projects/notes/discussions/commit", discussion_notes: discussion_notes = note.author.to_reference
started a discussion on
- if note.for_commit?
- commit = note.noteable
- if commit
commit
= link_to commit.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code), class: 'monospace'
- else
a deleted commit
- else
- if note.active?
= link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do
the diff
- else
an outdated diff
= time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "note-created-ago")
.discussion-actions
= link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
- if expanded
= icon("chevron-up")
- else
= icon("chevron-down")
Toggle discussion
.discussion-body.js-toggle-content{ class: ("hide" unless expanded) }
- if note.diff_note?
= render "projects/notes/discussions/diff_with_notes", discussion_notes: discussion_notes
- else
= render "projects/notes/discussions/notes", discussion_notes: discussion_notes
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
= f.hidden_field :line_code = f.hidden_field :line_code
= f.hidden_field :noteable_id = f.hidden_field :noteable_id
= f.hidden_field :noteable_type = f.hidden_field :noteable_type
= f.hidden_field :type
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text', placeholder: "Write a comment or drag your files here..." = render 'projects/zen', f: f, attr: :note, classes: 'note-textarea js-note-text', placeholder: "Write a comment or drag your files here..."
......
- return unless note.author
- return if note.cross_reference_not_visible_for?(current_user)
- note_editable = note_editable?(note) - note_editable = note_editable?(note)
%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable} } %li.timeline-entry{ id: dom_id(note), class: ["note", "note-row-#{note.id}", ('system-note' if note.system)], data: {author_id: note.author.id, editable: note_editable} }
.timeline-entry-inner .timeline-entry-inner
.timeline-icon .timeline-icon
%a{href: user_path(note.author)} %a{href: user_path(note.author)}
...@@ -8,8 +11,8 @@ ...@@ -8,8 +11,8 @@
.note-header .note-header
= link_to_member(note.project, note.author, avatar: false) = link_to_member(note.project, note.author, avatar: false)
.inline.note-headline-light .inline.note-headline-light
= "#{note.author.to_reference}" = note.author.to_reference
- if !note.system - unless note.system
commented commented
%a{ href: "##{dom_id(note)}" } %a{ href: "##{dom_id(note)}" }
= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago') = time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
......
...@@ -2,14 +2,9 @@ ...@@ -2,14 +2,9 @@
- @discussions.each do |discussion_notes| - @discussions.each do |discussion_notes|
- note = discussion_notes.first - note = discussion_notes.first
- if note_for_main_target?(note) - if note_for_main_target?(note)
- next if note.cross_reference_not_visible_for?(current_user) = render partial: "projects/notes/note", object: note, as: :note
= render discussion_notes
- else - else
= render 'projects/notes/discussion', discussion_notes: discussion_notes = render 'projects/notes/discussion', discussion_notes: discussion_notes
- else - else
- @notes.each do |note| - @notes.each do |note|
- next unless note.author = render partial: "projects/notes/note", object: note, as: :note
- next if note.cross_reference_not_visible_for?(current_user)
= render note
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
= link_to_member(@project, note.author, avatar: false)
.inline.discussion-headline-light
= "#{note.author.to_reference} started a discussion"
= link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do
on the diff
= time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago")
.discussion-actions
= link_to "#", class: "discussion-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-up
Show/hide discussion
.discussion-body.js-toggle-content
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
- note = discussion_notes.first
- commit = note.noteable
- commit_description = commit ? 'commit' : 'a deleted commit'
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
= link_to_member(@project, note.author, avatar: false)
.inline.discussion-headline-light
= "#{note.author.to_reference} started a discussion on #{commit_description}"
- if commit
= link_to(commit.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable), class: 'monospace')
= time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago")
.discussion-actions
= link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-up
Show/hide discussion
.discussion-body.js-toggle-content
- if note.for_diff_line?
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
- else
.panel.panel-default
.notes{ data: { discussion_id: discussion_notes.first.discussion_id } }
%ul.notes.timeline
= render discussion_notes
.discussion-reply-holder
= link_to_reply_diff(discussion_notes.first)
- diff = note.diff
- if diff
.diff-file
.diff-header
%span
- if diff.deleted_file
= diff.old_path
- else
= diff.new_path
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
.diff-content.code.js-syntax-highlight
%table
- note.truncated_diff_lines.each do |line|
- type = line.type
- line_code = generate_line_code(note.file_path, line)
%tr.line_holder{ id: line_code, class: "#{type}" }
- if type == "match"
%td.old_line.diff-line-num= "..."
%td.new_line.diff-line-num= "..."
%td.line_content.match= line.text
- else
%td.old_line.diff-line-num{ data: { linenumber: type == "new" ? "&nbsp;".html_safe : line.old_pos } }
%td.new_line.diff-line-num{ data: { linenumber: type == "old" ? "&nbsp;".html_safe : line.new_pos } }
%td.line_content{ class: ['noteable_line', type, line_code], line_code: line_code }= diff_line_content(line.text, type)
- if line_code == note.line_code
= render "projects/notes/diff_notes_with_reply", notes: discussion_notes
- note = discussion_notes.first
- diff = note.diff
- return unless diff
.diff-file
.diff-header
%span
- if diff.deleted_file
= diff.old_path
- else
= diff.new_path
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
.diff-content.code.js-syntax-highlight
%table
- note.truncated_diff_lines.each do |line|
- type = line.type
- line_code = generate_line_code(note.diff_file_path, line)
%tr.line_holder{ id: line_code, class: "#{type}" }
- if type == "match"
%td.old_line.diff-line-num= "..."
%td.new_line.diff-line-num= "..."
%td.line_content.match= line.text
- else
%td.old_line.diff-line-num{ data: { linenumber: type == "new" ? "&nbsp;".html_safe : line.old_pos } }
%td.new_line.diff-line-num{ data: { linenumber: type == "old" ? "&nbsp;".html_safe : line.new_pos } }
%td.line_content{ class: ['noteable_line', type, line_code], line_code: line_code }= diff_line_content(line.text, type)
- if line_code == note.line_code
= render "projects/notes/diff_notes_with_reply", notes: discussion_notes
- note = discussion_notes.first
.panel.panel-default
.notes{ data: { discussion_id: note.discussion_id } }
%ul.notes.timeline
= render partial: "projects/notes/note", collection: discussion_notes, as: :note
.discussion-reply-holder
= link_to_reply_discussion(note)
- note = discussion_notes.first
.discussion.js-toggle-container{ class: note.discussion_id }
.discussion-header
= link_to_member(@project, note.author, avatar: false)
.inline.discussion-headline-light
= "#{note.author.to_reference} started a discussion"
on the outdated diff
= time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "discussion_updated_ago")
.discussion-actions
= link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do
%i.fa.fa-chevron-down
Show/hide discussion
.discussion-body.js-toggle-content.hide
= render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note
...@@ -20,7 +20,7 @@ Rails.application.configure do ...@@ -20,7 +20,7 @@ Rails.application.configure do
config.action_dispatch.show_exceptions = false config.action_dispatch.show_exceptions = false
# Disable request forgery protection in test environment # Disable request forgery protection in test environment
config.action_controller.allow_forgery_protection = false config.action_controller.allow_forgery_protection = false
# Tell Action Mailer not to deliver emails to the real world. # Tell Action Mailer not to deliver emails to the real world.
# The :test delivery method accumulates sent emails in the # The :test delivery method accumulates sent emails in the
......
...@@ -98,6 +98,7 @@ production: &base ...@@ -98,6 +98,7 @@ production: &base
wiki: true wiki: true
snippets: false snippets: false
builds: true builds: true
container_registry: true
## Webhook settings ## Webhook settings
# Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10) # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10)
...@@ -175,6 +176,14 @@ production: &base ...@@ -175,6 +176,14 @@ production: &base
repository_archive_cache_worker: repository_archive_cache_worker:
cron: "0 * * * *" cron: "0 * * * *"
registry:
# enabled: true
# host: registry.example.com
# port: 5000
# api_url: http://localhost:5000/
# key: config/registry.key
# issuer: omnibus-certificate
# #
# 2. GitLab CI settings # 2. GitLab CI settings
# ========================== # ==========================
......
...@@ -126,7 +126,7 @@ end ...@@ -126,7 +126,7 @@ end
Settings['omniauth'] ||= Settingslogic.new({}) Settings['omniauth'] ||= Settingslogic.new({})
Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil? Settings.omniauth['enabled'] = false if Settings.omniauth['enabled'].nil?
Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil? Settings.omniauth['auto_sign_in_with_provider'] = false if Settings.omniauth['auto_sign_in_with_provider'].nil?
Settings.omniauth['allow_single_sign_on'] = false if Settings.omniauth['allow_single_sign_on'].nil? Settings.omniauth['allow_single_sign_on'] = false if Settings.omniauth['allow_single_sign_on'].nil?
Settings.omniauth['external_providers'] = [] if Settings.omniauth['external_providers'].nil? Settings.omniauth['external_providers'] = [] if Settings.omniauth['external_providers'].nil?
...@@ -134,7 +134,7 @@ Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block ...@@ -134,7 +134,7 @@ Settings.omniauth['block_auto_created_users'] = true if Settings.omniauth['block
Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil? Settings.omniauth['auto_link_ldap_user'] = false if Settings.omniauth['auto_link_ldap_user'].nil?
Settings.omniauth['auto_link_saml_user'] = false if Settings.omniauth['auto_link_saml_user'].nil? Settings.omniauth['auto_link_saml_user'] = false if Settings.omniauth['auto_link_saml_user'].nil?
Settings.omniauth['providers'] ||= [] Settings.omniauth['providers'] ||= []
Settings.omniauth['cas3'] ||= Settingslogic.new({}) Settings.omniauth['cas3'] ||= Settingslogic.new({})
Settings.omniauth.cas3['session_duration'] ||= 8.hours Settings.omniauth.cas3['session_duration'] ||= 8.hours
Settings.omniauth['session_tickets'] ||= Settingslogic.new({}) Settings.omniauth['session_tickets'] ||= Settingslogic.new({})
...@@ -168,7 +168,7 @@ end ...@@ -168,7 +168,7 @@ end
Settings['shared'] ||= Settingslogic.new({}) Settings['shared'] ||= Settingslogic.new({})
Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root) Settings.shared['path'] = File.expand_path(Settings.shared['path'] || "shared", Rails.root)
Settings['issues_tracker'] ||= {} Settings['issues_tracker'] ||= {}
# #
# GitLab # GitLab
...@@ -183,7 +183,7 @@ Settings.gitlab['ssh_host'] ||= Settings.gitlab.host ...@@ -183,7 +183,7 @@ Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80
Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || '' Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http" Settings.gitlab['protocol'] ||= Settings.gitlab.https ? "https" : "http"
Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil? Settings.gitlab['email_enabled'] ||= true if Settings.gitlab['email_enabled'].nil?
Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings.gitlab.host}" Settings.gitlab['email_from'] ||= ENV['GITLAB_EMAIL_FROM'] || "gitlab@#{Settings.gitlab.host}"
Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab' Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'GitLab'
...@@ -196,7 +196,7 @@ Settings.gitlab['user_home'] ||= begin ...@@ -196,7 +196,7 @@ Settings.gitlab['user_home'] ||= begin
rescue ArgumentError # no user configured rescue ArgumentError # no user configured
'/home/' + Settings.gitlab['user'] '/home/' + Settings.gitlab['user']
end end
Settings.gitlab['time_zone'] ||= nil Settings.gitlab['time_zone'] ||= nil
Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil? Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].nil?
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil? Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
...@@ -206,12 +206,13 @@ Settings.gitlab['default_projects_features'] ||= {} ...@@ -206,12 +206,13 @@ Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10 Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10 Settings.gitlab['max_attachment_size'] ||= 10
Settings.gitlab['session_expire_delay'] ||= 10080 Settings.gitlab['session_expire_delay'] ||= 10080
Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil? Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil?
Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil? Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil?
Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil?
Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil? Settings.gitlab.default_projects_features['builds'] = true if Settings.gitlab.default_projects_features['builds'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil?
Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil? Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
Settings.gitlab['restricted_signup_domains'] ||= [] Settings.gitlab['restricted_signup_domains'] ||= []
Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'] Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
...@@ -225,8 +226,8 @@ Settings['gitlab_ci'] ||= Settingslogic.new({}) ...@@ -225,8 +226,8 @@ Settings['gitlab_ci'] ||= Settingslogic.new({})
Settings.gitlab_ci['shared_runners_enabled'] = true if Settings.gitlab_ci['shared_runners_enabled'].nil? Settings.gitlab_ci['shared_runners_enabled'] = true if Settings.gitlab_ci['shared_runners_enabled'].nil?
Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil? Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil?
Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil? Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil?
Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url)
Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root) Settings.gitlab_ci['builds_path'] = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root)
Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url)
# #
# Reply by email # Reply by email
...@@ -240,7 +241,17 @@ Settings.incoming_email['enabled'] = false if Settings.incoming_email['enabled'] ...@@ -240,7 +241,17 @@ Settings.incoming_email['enabled'] = false if Settings.incoming_email['enabled']
Settings['artifacts'] ||= Settingslogic.new({}) Settings['artifacts'] ||= Settingslogic.new({})
Settings.artifacts['enabled'] = true if Settings.artifacts['enabled'].nil? Settings.artifacts['enabled'] = true if Settings.artifacts['enabled'].nil?
Settings.artifacts['path'] = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root) Settings.artifacts['path'] = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root)
Settings.artifacts['max_size'] ||= 100 # in megabytes Settings.artifacts['max_size'] ||= 100 # in megabytes
#
# Registry
#
Settings['registry'] ||= Settingslogic.new({})
Settings.registry['enabled'] ||= false
Settings.registry['host'] ||= "example.com"
Settings.registry['api_url'] ||= "http://localhost:5000/"
Settings.registry['key'] ||= nil
Settings.registry['issuer'] ||= nil
# #
# Git LFS # Git LFS
...@@ -298,7 +309,7 @@ Settings['backup'] ||= Settingslogic.new({}) ...@@ -298,7 +309,7 @@ Settings['backup'] ||= Settingslogic.new({})
Settings.backup['keep_time'] ||= 0 Settings.backup['keep_time'] ||= 0
Settings.backup['pg_schema'] = nil Settings.backup['pg_schema'] = nil
Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root) Settings.backup['path'] = File.expand_path(Settings.backup['path'] || "tmp/backups/", Rails.root)
Settings.backup['archive_permissions'] ||= 0600 Settings.backup['archive_permissions'] ||= 0600
Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil }) Settings.backup['upload'] ||= Settingslogic.new({ 'remote_directory' => nil, 'connection' => nil })
# Convert upload connection settings to use symbol keys, to make Fog happy # Convert upload connection settings to use symbol keys, to make Fog happy
if Settings.backup['upload']['connection'] if Settings.backup['upload']['connection']
......
...@@ -20,7 +20,7 @@ if File.exist?(aws_file) ...@@ -20,7 +20,7 @@ if File.exist?(aws_file)
config.fog_public = false config.fog_public = false
# optional, defaults to {} # optional, defaults to {}
config.fog_attributes = { 'Cache-Control'=>'max-age=315576000' } config.fog_attributes = { 'Cache-Control' => 'max-age=315576000' }
# optional time (in seconds) that authenticated urls will be valid. # optional time (in seconds) that authenticated urls will be valid.
# when fog_public is false and provider is AWS or Google, defaults to 600 # when fog_public is false and provider is AWS or Google, defaults to 600
......
...@@ -243,7 +243,7 @@ Devise.setup do |config| ...@@ -243,7 +243,7 @@ Devise.setup do |config|
when Hash when Hash
# Add procs for handling SLO # Add procs for handling SLO
if provider['name'] == 'cas3' if provider['name'] == 'cas3'
provider['args'][:on_single_sign_out] = lambda do |request| provider['args'][:on_single_sign_out] = lambda do |request|
ticket = request.params[:session_index] ticket = request.params[:session_index]
raise "Service Ticket not found." unless Gitlab::OAuth::Session.valid?(:cas3, ticket) raise "Service Ticket not found." unless Gitlab::OAuth::Session.valid?(:cas3, ticket)
Gitlab::OAuth::Session.destroy(:cas3, ticket) Gitlab::OAuth::Session.destroy(:cas3, ticket)
......
...@@ -64,6 +64,9 @@ Rails.application.routes.draw do ...@@ -64,6 +64,9 @@ Rails.application.routes.draw do
get 'search' => 'search#show' get 'search' => 'search#show'
get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete
# JSON Web Token
get 'jwt/auth' => 'jwt#auth'
# API # API
API::API.logger Rails.logger API::API.logger Rails.logger
mount API::API => '/api' mount API::API => '/api'
...@@ -74,7 +77,7 @@ Rails.application.routes.draw do ...@@ -74,7 +77,7 @@ Rails.application.routes.draw do
end end
# Health check # Health check
get 'health_check(/:checks)' => 'health_check#index', as: :health_check get 'health_check(/:checks)' => 'health_check#index', as: :health_check
# Enable Grack support # Enable Grack support
mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put] mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
...@@ -83,7 +86,7 @@ Rails.application.routes.draw do ...@@ -83,7 +86,7 @@ Rails.application.routes.draw do
get 'help' => 'help#index' get 'help' => 'help#index'
get 'help/:category/:file' => 'help#show', as: :help_page, constraints: { category: /.*/, file: /[^\/\.]+/ } get 'help/:category/:file' => 'help#show', as: :help_page, constraints: { category: /.*/, file: /[^\/\.]+/ }
get 'help/shortcuts' get 'help/shortcuts'
get 'help/ui' => 'help#ui' get 'help/ui' => 'help#ui'
# #
# Global snippets # Global snippets
......
class AddImagesEnabledForProject < ActiveRecord::Migration
def change
add_column :projects, :container_registry_enabled, :boolean
end
end
class AddTypeToNotes < ActiveRecord::Migration
def change
add_column :notes, :type, :string
end
end
class SetTypeOnLegacyDiffNotes < ActiveRecord::Migration
def change
execute "UPDATE notes SET type = 'LegacyDiffNote' WHERE line_code IS NOT NULL"
end
end
class AddSendUserConfirmationEmailToApplicationSettings < ActiveRecord::Migration
def up
add_column :application_settings, :send_user_confirmation_email, :boolean, default: false
#Sets confirmation email to true by default on existing installations.
execute "UPDATE application_settings SET send_user_confirmation_email=true"
end
def down
remove_column :application_settings, :send_user_confirmation_email
end
end
...@@ -632,10 +632,11 @@ ActiveRecord::Schema.define(version: 20160509201028) do ...@@ -632,10 +632,11 @@ ActiveRecord::Schema.define(version: 20160509201028) do
t.string "line_code" t.string "line_code"
t.string "commit_id" t.string "commit_id"
t.integer "noteable_id" t.integer "noteable_id"
t.boolean "system", default: false, null: false t.boolean "system", default: false, null: false
t.text "st_diff" t.text "st_diff"
t.integer "updated_by_id" t.integer "updated_by_id"
t.boolean "is_award", default: false, null: false t.boolean "is_award", default: false, null: false
t.string "type"
end end
add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
...@@ -761,6 +762,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do ...@@ -761,6 +762,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do
t.integer "pushes_since_gc", default: 0 t.integer "pushes_since_gc", default: 0
t.boolean "last_repository_check_failed" t.boolean "last_repository_check_failed"
t.datetime "last_repository_check_at" t.datetime "last_repository_check_at"
t.boolean "container_registry_enabled"
end end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
......
...@@ -58,4 +58,4 @@ to the naming scheme `GITLAB_#{name in 1_settings.rb in upper case}`. ...@@ -58,4 +58,4 @@ to the naming scheme `GITLAB_#{name in 1_settings.rb in upper case}`.
It's possible to preconfigure the GitLab docker image by adding the environment It's possible to preconfigure the GitLab docker image by adding the environment
variable `GITLAB_OMNIBUS_CONFIG` to the `docker run` command. variable `GITLAB_OMNIBUS_CONFIG` to the `docker run` command.
For more information see the ['preconfigure-docker-container' section in the Omnibus documentation](http://doc.gitlab.com/omnibus/docker/#preconfigure-docker-container). For more information see the ['preconfigure-docker-container' section in the Omnibus documentation](http://docs.gitlab.com/omnibus/docker/#preconfigure-docker-container).
...@@ -60,4 +60,4 @@ Read more on high-availability configuration: ...@@ -60,4 +60,4 @@ Read more on high-availability configuration:
configure custom domains with custom SSL, which would not be possible configure custom domains with custom SSL, which would not be possible
if SSL was terminated at the load balancer. if SSL was terminated at the load balancer.
[gitlab-pages]: http://doc.gitlab.com/ee/pages/administration.html [gitlab-pages]: http://docs.gitlab.com/ee/pages/administration.html
...@@ -113,4 +113,4 @@ Read more on high-availability configuration: ...@@ -113,4 +113,4 @@ Read more on high-availability configuration:
1. [Configure the GitLab application servers](gitlab.md) 1. [Configure the GitLab application servers](gitlab.md)
1. [Configure the load balancers](load_balancer.md) 1. [Configure the load balancers](load_balancer.md)
[udp-log-shipping]: http://doc.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only "UDP log shipping" [udp-log-shipping]: http://docs.gitlab.com/omnibus/settings/logs.html#udp-log-shipping-gitlab-enterprise-edition-only "UDP log shipping"
...@@ -424,6 +424,7 @@ Parameters: ...@@ -424,6 +424,7 @@ Parameters:
- `builds_enabled` (optional) - `builds_enabled` (optional)
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
...@@ -447,6 +448,7 @@ Parameters: ...@@ -447,6 +448,7 @@ Parameters:
- `builds_enabled` (optional) - `builds_enabled` (optional)
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `import_url` (optional) - `import_url` (optional)
...@@ -472,6 +474,7 @@ Parameters: ...@@ -472,6 +474,7 @@ Parameters:
- `builds_enabled` (optional) - `builds_enabled` (optional)
- `wiki_enabled` (optional) - `wiki_enabled` (optional)
- `snippets_enabled` (optional) - `snippets_enabled` (optional)
- `container_registry_enabled` (optional)
- `public` (optional) - if `true` same as setting visibility_level = 20 - `public` (optional) - if `true` same as setting visibility_level = 20
- `visibility_level` (optional) - `visibility_level` (optional)
- `public_builds` (optional) - `public_builds` (optional)
......
...@@ -491,7 +491,7 @@ Jira issue tracker ...@@ -491,7 +491,7 @@ Jira issue tracker
Set JIRA service for a project. Set JIRA service for a project.
> Setting `project_url`, `issues_url` and `new_issue_url` will allow a user to easily navigate to the Jira issue tracker. See the [integration doc](http://doc.gitlab.com/ce/integration/external-issue-tracker.html) for details. Support for referencing commits and automatic closing of Jira issues directly from GitLab is [available in GitLab EE.](http://doc.gitlab.com/ee/integration/jira.html) > Setting `project_url`, `issues_url` and `new_issue_url` will allow a user to easily navigate to the Jira issue tracker. See the [integration doc](http://docs.gitlab.com/ce/integration/external-issue-tracker.html) for details. Support for referencing commits and automatic closing of Jira issues directly from GitLab is [available in GitLab EE.](http://docs.gitlab.com/ee/integration/jira.html)
``` ```
PUT /projects/:id/services/jira PUT /projects/:id/services/jira
......
...@@ -127,7 +127,7 @@ Inside the document: ...@@ -127,7 +127,7 @@ Inside the document:
``` ```
If the document you are editing resides in a place other than the GitLab CE/EE If the document you are editing resides in a place other than the GitLab CE/EE
`doc/` directory, instead of the relative link, use the full path: `doc/` directory, instead of the relative link, use the full path:
`http://doc.gitlab.com/ce/administration/restart_gitlab.html`. `http://docs.gitlab.com/ce/administration/restart_gitlab.html`.
Replace `reconfigure` with `restart` where appropriate. Replace `reconfigure` with `restart` where appropriate.
## Installation guide ## Installation guide
...@@ -266,5 +266,5 @@ curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -d "restricted_signup_domai ...@@ -266,5 +266,5 @@ curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -d "restricted_signup_domai
[cURL]: http://curl.haxx.se/ "cURL website" [cURL]: http://curl.haxx.se/ "cURL website"
[single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html [single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html
[gfm]: http://doc.gitlab.com/ce/markdown/markdown.html#newlines "GitLab flavored markdown documentation" [gfm]: http://docs.gitlab.com/ce/markdown/markdown.html#newlines "GitLab flavored markdown documentation"
[doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation" [doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation"
...@@ -24,4 +24,4 @@ You may assign the Issue to a user, add a milestone and add labels (they are all ...@@ -24,4 +24,4 @@ You may assign the Issue to a user, add a milestone and add labels (they are all
![Submit new issue](basicsimages/submit_new_issue.png) ![Submit new issue](basicsimages/submit_new_issue.png)
Your Issue will now be added to the Issue Tracker and will be ready to be reviewed. You can comment on it and mention the people involved. You can also link Issues to the Merge Requests where the Issues are solved. To do this, you can use an [Issue closing pattern](http://doc.gitlab.com/ce/customization/issue_closing.html). Your Issue will now be added to the Issue Tracker and will be ready to be reviewed. You can comment on it and mention the people involved. You can also link Issues to the Merge Requests where the Issues are solved. To do this, you can use an [Issue closing pattern](http://docs.gitlab.com/ce/customization/issue_closing.html).
...@@ -14,7 +14,7 @@ Fill out the required information: ...@@ -14,7 +14,7 @@ Fill out the required information:
1. Select a [visibility level](https://gitlab.com/help/public_access/public_access) 1. Select a [visibility level](https://gitlab.com/help/public_access/public_access)
1. You can also [import your existing projects](http://doc.gitlab.com/ce/workflow/importing/README.html) 1. You can also [import your existing projects](http://docs.gitlab.com/ce/workflow/importing/README.html)
1. Click on "create project" 1. Click on "create project"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
**Note: Custom git hooks must be configured on the filesystem of the GitLab **Note: Custom git hooks must be configured on the filesystem of the GitLab
server. Only GitLab server administrators will be able to complete these tasks. server. Only GitLab server administrators will be able to complete these tasks.
Please explore [webhooks](../web_hooks/web_hooks.md) as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://doc.gitlab.com/ee/git_hooks/git_hooks.html).** Please explore [webhooks](../web_hooks/web_hooks.md) as an option if you do not have filesystem access. For a user configurable Git Hooks interface, please see [GitLab Enterprise Edition Git Hooks](http://docs.gitlab.com/ee/git_hooks/git_hooks.html).**
Git natively supports hooks that are executed on different actions. Git natively supports hooks that are executed on different actions.
Examples of server-side git hooks include pre-receive, post-receive, and update. Examples of server-side git hooks include pre-receive, post-receive, and update.
......
...@@ -6,7 +6,7 @@ Since an installation from source is a lot of work and error prone we strongly r ...@@ -6,7 +6,7 @@ Since an installation from source is a lot of work and error prone we strongly r
One reason the Omnibus package is more reliable is its use of Runit to restart any of the GitLab processes in case one crashes. One reason the Omnibus package is more reliable is its use of Runit to restart any of the GitLab processes in case one crashes.
On heavily used GitLab instances the memory usage of the Sidekiq background worker will grow over time. On heavily used GitLab instances the memory usage of the Sidekiq background worker will grow over time.
Omnibus packages solve this by [letting the Sidekiq terminate gracefully](http://doc.gitlab.com/ce/operations/sidekiq_memory_killer.html) if it uses too much memory. Omnibus packages solve this by [letting the Sidekiq terminate gracefully](http://docs.gitlab.com/ce/operations/sidekiq_memory_killer.html) if it uses too much memory.
After this termination Runit will detect Sidekiq is not running and will start it. After this termination Runit will detect Sidekiq is not running and will start it.
Since installations from source don't have Runit, Sidekiq can't be terminated and its memory usage will grow over time. Since installations from source don't have Runit, Sidekiq can't be terminated and its memory usage will grow over time.
......
...@@ -132,5 +132,5 @@ To disable the relative URL: ...@@ -132,5 +132,5 @@ To disable the relative URL:
1. Follow the same as above starting from 2. and set up the 1. Follow the same as above starting from 2. and set up the
GitLab URL to one that doesn't contain a relative path. GitLab URL to one that doesn't contain a relative path.
[omnibus-rel]: http://doc.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab" [omnibus-rel]: http://docs.gitlab.com/omnibus/settings/configuration.html#configuring-a-relative-url-for-gitlab "How to setup relative URL in Omnibus GitLab"
[restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab" [restart gitlab]: ../administration/restart_gitlab.md#installations-from-source "How to restart GitLab"
...@@ -19,7 +19,7 @@ See the documentation below for details on how to configure these services. ...@@ -19,7 +19,7 @@ See the documentation below for details on how to configure these services.
GitLab Enterprise Edition contains [advanced Jenkins support][jenkins]. GitLab Enterprise Edition contains [advanced Jenkins support][jenkins].
[jenkins]: http://doc.gitlab.com/ee/integration/jenkins.html [jenkins]: http://docs.gitlab.com/ee/integration/jenkins.html
## Project services ## Project services
......
...@@ -39,4 +39,4 @@ Install and update your GitLab installation. ...@@ -39,4 +39,4 @@ Install and update your GitLab installation.
- [Install GitLab](https://about.gitlab.com/installation/) - [Install GitLab](https://about.gitlab.com/installation/)
- [Update GitLab](https://about.gitlab.com/update/) - [Update GitLab](https://about.gitlab.com/update/)
- [Explore Omnibus GitLab configuration options](http://doc.gitlab.com/omnibus/settings/configuration.html) - [Explore Omnibus GitLab configuration options](http://docs.gitlab.com/omnibus/settings/configuration.html)
## Log system ## Log system
GitLab has advanced log system so everything is logging and you can analize your instance using various system log files. GitLab has advanced log system so everything is logging and you can analize your instance using various system log files.
In addition to system log files, GitLab Enterprise Edition comes with Audit Events. Find more about them [in Audit Events documentation](http://doc.gitlab.com/ee/administration/audit_events.html) In addition to system log files, GitLab Enterprise Edition comes with Audit Events. Find more about them [in Audit Events documentation](http://docs.gitlab.com/ee/administration/audit_events.html)
System log files are typically plain text in a standard log file format. This guide talks about how to read and use these system log files. System log files are typically plain text in a standard log file format. This guide talks about how to read and use these system log files.
......
...@@ -27,6 +27,7 @@ documentation](../workflow/add-user/add-user.md). ...@@ -27,6 +27,7 @@ documentation](../workflow/add-user/add-user.md).
| Manage issue tracker | | ✓ | ✓ | ✓ | ✓ | | Manage issue tracker | | ✓ | ✓ | ✓ | ✓ |
| Manage labels | | ✓ | ✓ | ✓ | ✓ | | Manage labels | | ✓ | ✓ | ✓ | ✓ |
| See a commit status | | ✓ | ✓ | ✓ | ✓ | | See a commit status | | ✓ | ✓ | ✓ | ✓ |
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| Manage merge requests | | | ✓ | ✓ | ✓ | | Manage merge requests | | | ✓ | ✓ | ✓ |
| Create new merge request | | | ✓ | ✓ | ✓ | | Create new merge request | | | ✓ | ✓ | ✓ |
| Create new branches | | | ✓ | ✓ | ✓ | | Create new branches | | | ✓ | ✓ | ✓ |
...@@ -37,6 +38,7 @@ documentation](../workflow/add-user/add-user.md). ...@@ -37,6 +38,7 @@ documentation](../workflow/add-user/add-user.md).
| Write a wiki | | | ✓ | ✓ | ✓ | | Write a wiki | | | ✓ | ✓ | ✓ |
| Cancel and retry builds | | | ✓ | ✓ | ✓ | | Cancel and retry builds | | | ✓ | ✓ | ✓ |
| Create or update commit status | | | ✓ | ✓ | ✓ | | Create or update commit status | | | ✓ | ✓ | ✓ |
| Update a container registry | | | ✓ | ✓ | ✓ |
| Create new milestones | | | | ✓ | ✓ | | Create new milestones | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ | | Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ | | Push to protected branches | | | | ✓ | ✓ |
......
...@@ -8,4 +8,4 @@ ...@@ -8,4 +8,4 @@
- [User management](user_management.md) - [User management](user_management.md)
- [Webhooks](web_hooks.md) - [Webhooks](web_hooks.md)
- [Import](import.md) of git repositories in bulk - [Import](import.md) of git repositories in bulk
- [Rebuild authorized_keys file](http://doc.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators - [Rebuild authorized_keys file](http://docs.gitlab.com/ce/raketasks/maintenance.html#rebuild-authorized_keys-file) task for administrators
...@@ -8,3 +8,4 @@ ...@@ -8,3 +8,4 @@
- [User File Uploads](user_file_uploads.md) - [User File Uploads](user_file_uploads.md)
- [How we manage the CRIME vulnerability](crime_vulnerability.md) - [How we manage the CRIME vulnerability](crime_vulnerability.md)
- [Enforce Two-factor authentication](two_factor_authentication.md) - [Enforce Two-factor authentication](two_factor_authentication.md)
- [Send email confirmation on sign-up](user_email_confirmation.md)
# User email confirmation at sign-up
Gitlab admin can enable email confirmation on sign-up, if you want to confirm all
user emails before they are able to sign-in.
In the Admin area under **Settings** (`/admin/application_settings`), go to section
**Sign-in Restrictions** and look for **Send confirmation email on sign-up** option.
...@@ -29,7 +29,7 @@ Based on your installation, choose a section below that fits your needs. ...@@ -29,7 +29,7 @@ Based on your installation, choose a section below that fits your needs.
## Omnibus Packages ## Omnibus Packages
- The [Omnibus update guide](http://doc.gitlab.com/omnibus/update/README.html) - The [Omnibus update guide](http://docs.gitlab.com/omnibus/update/README.html)
contains the steps needed to update an Omnibus GitLab package. contains the steps needed to update an Omnibus GitLab package.
## Installation from source ## Installation from source
...@@ -86,10 +86,10 @@ possible. ...@@ -86,10 +86,10 @@ possible.
information about configuring GitLab to work with a MySQL database. information about configuring GitLab to work with a MySQL database.
- [Restoring from backup after a failed upgrade](restore_after_failure.md) - [Restoring from backup after a failed upgrade](restore_after_failure.md)
[omnidocker]: http://doc.gitlab.com/omnibus/docker/README.html [omnidocker]: http://docs.gitlab.com/omnibus/docker/README.html
[source-ee]: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/doc/update [source-ee]: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/doc/update
[source-ce]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update [source-ce]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update
[ee-ce]: ../downgrade_ee_to_ce/README.md [ee-ce]: ../downgrade_ee_to_ce/README.md
[ce]: https://about.gitlab.com/features/#community [ce]: https://about.gitlab.com/features/#community
[ee]: https://about.gitlab.com/features/#enterprise [ee]: https://about.gitlab.com/features/#enterprise
[omni-ce-ee]: http://doc.gitlab.com/omnibus/update/README.html#from-community-edition-to-enterprise-edition [omni-ce-ee]: http://docs.gitlab.com/omnibus/update/README.html#from-community-edition-to-enterprise-edition
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