Commit 8f86d931 authored by tiagonbotelho's avatar tiagonbotelho

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce

parents 11ffdea7 4e40ba93
image: "ruby:2.1" image: "ruby:2.1"
services:
- mysql:latest
- redis:alpine
cache: cache:
key: "ruby21" key: "ruby21"
paths: paths:
...@@ -34,7 +30,6 @@ stages: ...@@ -34,7 +30,6 @@ stages:
- post-test - post-test
# Prepare and merge knapsack tests # Prepare and merge knapsack tests
.knapsack-state: &knapsack-state .knapsack-state: &knapsack-state
services: [] services: []
variables: variables:
...@@ -68,8 +63,14 @@ update-knapsack: ...@@ -68,8 +63,14 @@ update-knapsack:
# Execute all testing suites # Execute all testing suites
.use-db: &use-db
services:
- mysql:latest
- redis:alpine
.rspec-knapsack: &rspec-knapsack .rspec-knapsack: &rspec-knapsack
stage: test stage: test
<<: *use-db
script: script:
- bundle exec rake assets:precompile 2>/dev/null - bundle exec rake assets:precompile 2>/dev/null
- JOB_NAME=( $CI_BUILD_NAME ) - JOB_NAME=( $CI_BUILD_NAME )
...@@ -85,6 +86,7 @@ update-knapsack: ...@@ -85,6 +86,7 @@ update-knapsack:
.spinach-knapsack: &spinach-knapsack .spinach-knapsack: &spinach-knapsack
stage: test stage: test
<<: *use-db
script: script:
- bundle exec rake assets:precompile 2>/dev/null - bundle exec rake assets:precompile 2>/dev/null
- JOB_NAME=( $CI_BUILD_NAME ) - JOB_NAME=( $CI_BUILD_NAME )
...@@ -133,6 +135,7 @@ spinach 9 10: *spinach-knapsack ...@@ -133,6 +135,7 @@ spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.3 # Execute all testing suites against Ruby 2.3
.ruby-23: &ruby-23 .ruby-23: &ruby-23
image: "ruby:2.3" image: "ruby:2.3"
<<: *use-db
only: only:
- master - master
cache: cache:
...@@ -183,22 +186,41 @@ spinach 9 10 ruby23: *spinach-knapsack-ruby23 ...@@ -183,22 +186,41 @@ spinach 9 10 ruby23: *spinach-knapsack-ruby23
# Other generic tests # Other generic tests
.static-analyses-variables: &static-analyses-variables
variables:
SIMPLECOV: "false"
USE_DB: "false"
USE_BUNDLE_INSTALL: "true"
.exec: &exec .exec: &exec
<<: *static-analyses-variables
stage: test stage: test
script: script:
- bundle exec $CI_BUILD_NAME - bundle exec $CI_BUILD_NAME
teaspoon: *exec
rubocop: *exec rubocop: *exec
rake scss_lint: *exec rake scss_lint: *exec
rake brakeman: *exec rake brakeman: *exec
rake flog: *exec rake flog: *exec
rake flay: *exec rake flay: *exec
rake db:migrate:reset: *exec
license_finder: *exec license_finder: *exec
rake downtime_check: *exec
rake db:migrate:reset:
stage: test
<<: *use-db
script:
- rake db:migrate:reset
teaspoon:
stage: test
<<: *use-db
script:
- teaspoon
bundler:audit: bundler:audit:
stage: test stage: test
<<: *static-analyses-variables
only: only:
- master - master
script: script:
......
...@@ -291,6 +291,10 @@ Style/MultilineMethodDefinitionBraceLayout: ...@@ -291,6 +291,10 @@ Style/MultilineMethodDefinitionBraceLayout:
Style/MultilineOperationIndentation: Style/MultilineOperationIndentation:
Enabled: false Enabled: false
# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
Style/MultilineTernaryOperator:
Enabled: true
# Favor unless over if for negative conditions (or control flow or). # Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf: Style/NegatedIf:
Enabled: true Enabled: true
......
...@@ -226,10 +226,6 @@ Style/LineEndConcatenation: ...@@ -226,10 +226,6 @@ Style/LineEndConcatenation:
Style/MethodCallParentheses: Style/MethodCallParentheses:
Enabled: false Enabled: false
# Offense count: 3
Style/MultilineTernaryOperator:
Enabled: false
# Offense count: 62 # Offense count: 62
# Cop supports --auto-correct. # Cop supports --auto-correct.
Style/MutableConstant: Style/MutableConstant:
......
This diff is collapsed.
...@@ -349,6 +349,3 @@ gem 'health_check', '~> 2.1.0' ...@@ -349,6 +349,3 @@ gem 'health_check', '~> 2.1.0'
# System information # System information
gem 'vmstat', '~> 2.1.0' gem 'vmstat', '~> 2.1.0'
gem 'sys-filesystem', '~> 1.1.6' gem 'sys-filesystem', '~> 1.1.6'
# Secure headers for Content Security Policy
gem 'secure_headers', '~> 3.3'
...@@ -645,8 +645,6 @@ GEM ...@@ -645,8 +645,6 @@ GEM
sdoc (0.3.20) sdoc (0.3.20)
json (>= 1.1.3) json (>= 1.1.3)
rdoc (~> 3.10) rdoc (~> 3.10)
secure_headers (3.3.2)
useragent
seed-fu (2.3.6) seed-fu (2.3.6)
activerecord (>= 3.1) activerecord (>= 3.1)
activesupport (>= 3.1) activesupport (>= 3.1)
...@@ -769,7 +767,6 @@ GEM ...@@ -769,7 +767,6 @@ GEM
get_process_mem (~> 0) get_process_mem (~> 0)
unicorn (>= 4, < 6) unicorn (>= 4, < 6)
uniform_notifier (1.9.0) uniform_notifier (1.9.0)
useragent (0.16.7)
uuid (2.3.8) uuid (2.3.8)
macaddr (~> 1.0) macaddr (~> 1.0)
version_sorter (2.0.0) version_sorter (2.0.0)
...@@ -947,7 +944,6 @@ DEPENDENCIES ...@@ -947,7 +944,6 @@ DEPENDENCIES
sass-rails (~> 5.0.0) sass-rails (~> 5.0.0)
scss_lint (~> 0.47.0) scss_lint (~> 0.47.0)
sdoc (~> 0.3.20) sdoc (~> 0.3.20)
secure_headers (~> 3.3)
seed-fu (~> 2.3.5) seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
sentry-raven (~> 1.1.0) sentry-raven (~> 1.1.0)
......
...@@ -38,3 +38,14 @@ class @Admin ...@@ -38,3 +38,14 @@ class @Admin
$('li.group_member').bind 'ajax:success', -> $('li.group_member').bind 'ajax:success', ->
Turbolinks.visit(location.href) Turbolinks.visit(location.href)
showBlacklistType = ->
if $("input[name='blacklist_type']:checked").val() == 'file'
$('.blacklist-file').show()
$('.blacklist-raw').hide()
else
$('.blacklist-file').hide()
$('.blacklist-raw').show()
$("input[name='blacklist_type']").click showBlacklistType
showBlacklistType()
...@@ -7,7 +7,6 @@ class @FilesCommentButton ...@@ -7,7 +7,6 @@ class @FilesCommentButton
UNFOLDABLE_LINE_CLASS = 'js-unfold' UNFOLDABLE_LINE_CLASS = 'js-unfold'
EMPTY_CELL_CLASS = 'empty-cell' EMPTY_CELL_CLASS = 'empty-cell'
OLD_LINE_CLASS = 'old_line' OLD_LINE_CLASS = 'old_line'
NEW_CLASS = 'new'
LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content" LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
TEXT_FILE_SELECTOR = '.text-file' TEXT_FILE_SELECTOR = '.text-file'
DEBOUNCE_TIMEOUT_DURATION = 100 DEBOUNCE_TIMEOUT_DURATION = 100
...@@ -18,6 +17,8 @@ class @FilesCommentButton ...@@ -18,6 +17,8 @@ class @FilesCommentButton
debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION
$(document) $(document)
.off 'mouseover', LINE_COLUMN_CLASSES
.off 'mouseleave', LINE_COLUMN_CLASSES
.on 'mouseover', LINE_COLUMN_CLASSES, debounce .on 'mouseover', LINE_COLUMN_CLASSES, debounce
.on 'mouseleave', LINE_COLUMN_CLASSES, @destroy .on 'mouseleave', LINE_COLUMN_CLASSES, @destroy
...@@ -64,20 +65,20 @@ class @FilesCommentButton ...@@ -64,20 +65,20 @@ class @FilesCommentButton
getLineContent: (hoveredElement) -> getLineContent: (hoveredElement) ->
return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS
$(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent()) if @VIEW_TYPE is 'inline'
return $(hoveredElement).closest(LINE_HOLDER_CLASS).find ".#{LINE_CONTENT_CLASS}"
else
return $(hoveredElement).next ".#{LINE_CONTENT_CLASS}"
getButtonParent: (hoveredElement) -> getButtonParent: (hoveredElement) ->
if @VIEW_TYPE is 'inline' if @VIEW_TYPE is 'inline'
return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS
$(".#{OLD_LINE_CLASS}", hoveredElement.parent()) hoveredElement.parent().find ".#{OLD_LINE_CLASS}"
else else
return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS
$(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent()) $(hoveredElement).prev ".#{LINE_NUMBER_CLASS}"
diffTypeClass: (hoveredElement) ->
if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
isMovingToSameType: (e) -> isMovingToSameType: (e) ->
newButtonParent = @getButtonParent $(e.toElement) newButtonParent = @getButtonParent $(e.toElement)
......
...@@ -210,9 +210,22 @@ class GitLabDropdown ...@@ -210,9 +210,22 @@ class GitLabDropdown
data: => data: =>
return @fullData return @fullData
callback: (data) => callback: (data) =>
currentIndex = -1
@parseData data @parseData data
unless @filterInput.val() is ''
selector = '.dropdown-content li:not(.divider):visible'
if @dropdown.find('.dropdown-toggle-page').length
selector = ".dropdown-page-one #{selector}"
$(selector, @dropdown)
.first()
.find('a')
.addClass('is-focused')
currentIndex = 0
# Event listeners # Event listeners
@dropdown.on "shown.bs.dropdown", @opened @dropdown.on "shown.bs.dropdown", @opened
......
...@@ -55,8 +55,11 @@ class @MergeRequestWidget ...@@ -55,8 +55,11 @@ class @MergeRequestWidget
$('.mr-state-widget').replaceWith(data) $('.mr-state-widget').replaceWith(data)
ciLabelForStatus: (status) -> ciLabelForStatus: (status) ->
if status is 'success' switch status
when 'success'
'passed' 'passed'
when 'success_with_warnings'
'passed with warnings'
else else
status status
...@@ -116,7 +119,7 @@ class @MergeRequestWidget ...@@ -116,7 +119,7 @@ class @MergeRequestWidget
showCIStatus: (state) -> showCIStatus: (state) ->
return if not state? return if not state?
$('.ci_widget').hide() $('.ci_widget').hide()
allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"] allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"]
if state in allowed_states if state in allowed_states
$('.ci_widget.ci-' + state).show() $('.ci_widget.ci-' + state).show()
switch state switch state
...@@ -124,7 +127,7 @@ class @MergeRequestWidget ...@@ -124,7 +127,7 @@ class @MergeRequestWidget
@setMergeButtonClass('btn-danger') @setMergeButtonClass('btn-danger')
when "running" when "running"
@setMergeButtonClass('btn-warning') @setMergeButtonClass('btn-warning')
when "success" when "success", "success_with_warnings"
@setMergeButtonClass('btn-create') @setMergeButtonClass('btn-create')
else else
$('.ci_widget.ci-error').show() $('.ci_widget.ci-error').show()
......
...@@ -120,6 +120,9 @@ class @Sidebar ...@@ -120,6 +120,9 @@ class @Sidebar
i.show() i.show()
sidebarCollapseClicked: (e) -> sidebarCollapseClicked: (e) ->
return if $(e.currentTarget).hasClass('dont-change-state')
sidebar = e.data sidebar = e.data
e.preventDefault() e.preventDefault()
$block = $(@).closest('.block') $block = $(@).closest('.block')
......
...@@ -232,7 +232,9 @@ ...@@ -232,7 +232,9 @@
.nav-block { .nav-block {
.controls { .controls {
float: right; float: right;
margin-top: 11px; margin-top: 8px;
padding-bottom: 7px;
border-bottom: 1px solid $border-color;
} }
} }
......
...@@ -98,12 +98,29 @@ ...@@ -98,12 +98,29 @@
.md { .md {
&.md-preview-holder { &.md-preview-holder {
// Reset ul style types since we're nested inside a ul already
@include bulleted-list;
}
// On diffs code should wrap nicely and not overflow
code { code {
white-space: pre-wrap; white-space: pre-wrap;
word-break: keep-all; word-break: keep-all;
} }
@include bulleted-list; hr {
// Darken 'whitesmoke' a bit to make it more visible in note bodies
border-color: darken(#f5f5f5, 8%);
margin: 10px 0;
}
// Border around images in issue and MR comments.
img:not(.emoji) {
border: 1px solid $table-border-gray;
padding: 5px;
margin: 5px 0;
// Ensure that image does not exceed viewport
max-height: calc(100vh - 100px);
} }
} }
......
...@@ -176,3 +176,11 @@ ...@@ -176,3 +176,11 @@
} }
} }
} }
// hide event scope (namespace + project) where it is not necessary
.project-activity {
.event-scope {
display: none;
}
}
...@@ -70,6 +70,14 @@ ...@@ -70,6 +70,14 @@
color: $gl-success; color: $gl-success;
} }
&.ci-success_with_warnings {
color: $gl-success;
i {
color: $gl-warning;
}
}
&.ci-skipped { &.ci-skipped {
background-color: #eee; background-color: #eee;
color: #888; color: #888;
......
...@@ -91,34 +91,11 @@ ul.notes { ...@@ -91,34 +91,11 @@ ul.notes {
// Reset ul style types since we're nested inside a ul already // Reset ul style types since we're nested inside a ul already
@include bulleted-list; @include bulleted-list;
// On diffs code should wrap nicely and not overflow
code {
white-space: pre-wrap;
}
ul.task-list { ul.task-list {
ul:not(.task-list) { ul:not(.task-list) {
padding-left: 1.3em; padding-left: 1.3em;
} }
} }
hr {
// Darken 'whitesmoke' a bit to make it more visible in note bodies
border-color: darken(#f5f5f5, 8%);
margin: 10px 0;
}
code {
word-break: keep-all;
}
// Border around images in issue and MR comments.
img:not(.emoji) {
border: 1px solid $table-border-gray;
padding: 5px;
margin: 5px 0;
max-height: calc(100vh - 100px);
}
} }
} }
......
...@@ -333,13 +333,47 @@ a.deploy-project-label { ...@@ -333,13 +333,47 @@ a.deploy-project-label {
} }
.fork-namespaces { .fork-namespaces {
.row {
-webkit-flex-wrap: wrap;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
.fork-thumbnail { .fork-thumbnail {
@include border-radius($border-radius-base);
background-color: $white-light;
border: 1px solid $border-white-light;
height: 202px;
margin: $gl-padding;
text-align: center; text-align: center;
margin-bottom: $gl-padding; width: 169px;
&:hover, &.forked {
background-color: $row-hover;
border-color: $row-hover-border;
}
.no-avatar {
width: 100px;
height: 100px;
background-color: $gray-light;
border: 1px solid $gray-dark;
margin: 0 auto;
@include border-radius(50%);
i {
font-size: 100px;
color: $gray-dark;
}
}
a {
display: block;
width: 100%;
height: 100%;
padding-top: $gl-padding;
color: $gl-gray;
.caption { .caption {
padding: $gl-padding 0;
min-height: 30px; min-height: 30px;
padding: $gl-padding 0;
}
} }
img { img {
...@@ -347,6 +381,7 @@ a.deploy-project-label { ...@@ -347,6 +381,7 @@ a.deploy-project-label {
max-width: 100px; max-width: 100px;
} }
} }
}
} }
.project-import { .project-import {
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
border-color: $gl-danger; border-color: $gl-danger;
} }
&.ci-success { &.ci-success,
&.ci-success_with_warnings {
color: $gl-success; color: $gl-success;
border-color: $gl-success; border-color: $gl-success;
} }
...@@ -57,9 +58,12 @@ ...@@ -57,9 +58,12 @@
.ci-status-icon-failed { .ci-status-icon-failed {
color: $gl-danger; color: $gl-danger;
} }
.ci-status-icon-pending {
.ci-status-icon-pending,
.ci-status-icon-success_with_warning {
color: $gl-warning; color: $gl-warning;
} }
.ci-status-icon-running { .ci-status-icon-running {
color: $blue-normal; color: $blue-normal;
} }
......
...@@ -64,6 +64,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -64,6 +64,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
params[:application_setting][:disabled_oauth_sign_in_sources] = params[:application_setting][:disabled_oauth_sign_in_sources] =
AuthHelper.button_based_providers.map(&:to_s) - AuthHelper.button_based_providers.map(&:to_s) -
Array(enabled_oauth_sign_in_sources) Array(enabled_oauth_sign_in_sources)
params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file]
params.require(:application_setting).permit( params.require(:application_setting).permit(
:default_projects_limit, :default_projects_limit,
...@@ -83,7 +84,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -83,7 +84,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:default_project_visibility, :default_project_visibility,
:default_snippet_visibility, :default_snippet_visibility,
:default_group_visibility, :default_group_visibility,
:restricted_signup_domains_raw, :domain_whitelist_raw,
:domain_blacklist_enabled,
:domain_blacklist_raw,
:domain_blacklist_file,
:version_check_enabled, :version_check_enabled,
:admin_notification_email, :admin_notification_email,
:user_oauth_applications, :user_oauth_applications,
......
...@@ -60,6 +60,6 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -60,6 +60,6 @@ class Admin::GroupsController < Admin::ApplicationController
end end
def group_params def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level) params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level, :request_access_enabled)
end end
end end
class Admin::ServicesController < Admin::ApplicationController class Admin::ServicesController < Admin::ApplicationController
include ServiceParams
before_action :service, only: [:edit, :update] before_action :service, only: [:edit, :update]
def index def index
...@@ -13,7 +15,7 @@ class Admin::ServicesController < Admin::ApplicationController ...@@ -13,7 +15,7 @@ class Admin::ServicesController < Admin::ApplicationController
end end
def update def update
if service.update_attributes(application_services_params[:service]) if service.update_attributes(service_params[:service])
redirect_to admin_application_settings_services_path, redirect_to admin_application_settings_services_path,
notice: 'Application settings saved successfully' notice: 'Application settings saved successfully'
else else
...@@ -37,15 +39,4 @@ class Admin::ServicesController < Admin::ApplicationController ...@@ -37,15 +39,4 @@ class Admin::ServicesController < Admin::ApplicationController
def service def service
@service ||= Service.where(id: params[:id], template: true).first @service ||= Service.where(id: params[:id], template: true).first
end end
def application_services_params
application_services_params = params.permit(:id,
service: Projects::ServicesController::ALLOWED_PARAMS)
if application_services_params[:service].is_a?(Hash)
Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param|
application_services_params[:service].delete(param) if application_services_params[:service][param].blank?
end
end
application_services_params
end
end end
module ServiceParams
extend ActiveSupport::Concern
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :build_events, :wiki_page_events,
:notify_only_broken_builds, :add_pusher,
:send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
def service_params
dynamic_params = []
dynamic_params.concat(@service.event_channel_names)
service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params)
if service_params[:service].is_a?(Hash)
FILTER_BLANK_PARAMS.each do |param|
service_params[:service].delete(param) if service_params[:service][param].blank?
end
end
service_params
end
end
...@@ -121,7 +121,7 @@ class GroupsController < Groups::ApplicationController ...@@ -121,7 +121,7 @@ class GroupsController < Groups::ApplicationController
end end
def group_params def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock) params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock, :request_access_enabled)
end end
def load_events def load_events
......
...@@ -30,7 +30,7 @@ class HelpController < ApplicationController ...@@ -30,7 +30,7 @@ class HelpController < ApplicationController
end end
# Allow access to images in the doc folder # Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do format.any(:png, :gif, :jpeg, :mp4) do
# Note: We are purposefully NOT using `Rails.root.join` # Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}") path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}")
......
...@@ -3,11 +3,6 @@ class Projects::BadgesController < Projects::ApplicationController ...@@ -3,11 +3,6 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:index] before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index] before_action :no_cache_headers, except: [:index]
def index
@ref = params[:ref] || @project.default_branch || 'master'
@build_badge = Gitlab::Badge::Build.new(@project, @ref)
end
def build def build
badge = Gitlab::Badge::Build.new(project, params[:ref]) badge = Gitlab::Badge::Build.new(project, params[:ref])
......
...@@ -15,6 +15,7 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -15,6 +15,7 @@ class Projects::CompareController < Projects::ApplicationController
end end
def show def show
apply_diff_view_cookie!
end end
def diff_for_path def diff_for_path
......
...@@ -286,6 +286,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -286,6 +286,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
status = pipeline.status status = pipeline.status
coverage = pipeline.try(:coverage) coverage = pipeline.try(:coverage)
status = "success_with_warnings" if pipeline.success? && pipeline.has_warnings?
status ||= "preparing" status ||= "preparing"
else else
ci_service = @merge_request.source_project.ci_service ci_service = @merge_request.source_project.ci_service
......
class Projects::PipelinesSettingsController < Projects::ApplicationController
before_action :authorize_admin_pipeline!
def show
@ref = params[:ref] || @project.default_branch || 'master'
@build_badge = Gitlab::Badge::Build.new(@project, @ref)
end
def update
if @project.update_attributes(update_params)
flash[:notice] = "CI/CD Pipelines settings for '#{@project.name}' were successfully updated."
redirect_to namespace_project_pipelines_settings_path(@project.namespace, @project)
else
render 'index'
end
end
private
def create_params
params.require(:pipeline).permit(:ref)
end
def update_params
params.require(:project).permit(
:runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
:public_builds
)
end
end
...@@ -25,7 +25,7 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -25,7 +25,7 @@ class Projects::RefsController < Projects::ApplicationController
when "graphs_commits" when "graphs_commits"
commits_namespace_project_graph_path(@project.namespace, @project, @id) commits_namespace_project_graph_path(@project.namespace, @project, @id)
when "badges" when "badges"
namespace_project_badges_path(@project.namespace, @project, ref: @id) namespace_project_pipelines_settings_path(@project.namespace, @project, ref: @id)
else else
namespace_project_commits_path(@project.namespace, @project, @id) namespace_project_commits_path(@project.namespace, @project, @id)
end end
......
class Projects::ServicesController < Projects::ApplicationController class Projects::ServicesController < Projects::ApplicationController
ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain, include ServiceParams
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
:push_events, :issues_events, :merge_requests_events, :tag_push_events,
:note_events, :build_events, :wiki_page_events,
:notify_only_broken_builds, :add_pusher,
:send_from_committer_email, :disable_diffs, :external_wiki_url,
:notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id]
# Parameters to ignore if no value is specified
FILTER_BLANK_PARAMS = [:password]
# Authorize # Authorize
before_action :authorize_admin_project! before_action :authorize_admin_project!
...@@ -33,7 +18,7 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -33,7 +18,7 @@ class Projects::ServicesController < Projects::ApplicationController
end end
def update def update
if @service.update_attributes(service_params) if @service.update_attributes(service_params[:service])
redirect_to( redirect_to(
edit_namespace_project_service_path(@project.namespace, @project, edit_namespace_project_service_path(@project.namespace, @project,
@service.to_param, notice: @service.to_param, notice:
...@@ -64,12 +49,4 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -64,12 +49,4 @@ class Projects::ServicesController < Projects::ApplicationController
def service def service
@service ||= @project.services.find { |service| service.to_param == params[:id] } @service ||= @project.services.find { |service| service.to_param == params[:id] }
end end
def service_params
service_params = params.require(:service).permit(ALLOWED_PARAMS)
FILTER_BLANK_PARAMS.each do |param|
service_params.delete(param) if service_params[param].blank?
end
service_params
end
end end
class Projects::UploadsController < Projects::ApplicationController class Projects::UploadsController < Projects::ApplicationController
skip_before_action :reject_blocked!, :project, skip_before_action :reject_blocked!, :project,
:repository, if: -> { action_name == 'show' && image? } :repository, if: -> { action_name == 'show' && image_or_video? }
before_action :authorize_upload_file!, only: [:create] before_action :authorize_upload_file!, only: [:create]
...@@ -24,7 +24,7 @@ class Projects::UploadsController < Projects::ApplicationController ...@@ -24,7 +24,7 @@ class Projects::UploadsController < Projects::ApplicationController
def show def show
return render_404 if uploader.nil? || !uploader.file.exists? return render_404 if uploader.nil? || !uploader.file.exists?
disposition = uploader.image? ? 'inline' : 'attachment' disposition = uploader.image_or_video? ? 'inline' : 'attachment'
send_file uploader.file.path, disposition: disposition send_file uploader.file.path, disposition: disposition
end end
...@@ -49,7 +49,7 @@ class Projects::UploadsController < Projects::ApplicationController ...@@ -49,7 +49,7 @@ class Projects::UploadsController < Projects::ApplicationController
@uploader @uploader
end end
def image? def image_or_video?
uploader && uploader.file.exists? && uploader.image? uploader && uploader.file.exists? && uploader.image_or_video?
end end
end end
...@@ -296,7 +296,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -296,7 +296,7 @@ class ProjectsController < Projects::ApplicationController
:issues_tracker_id, :default_branch, :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, :only_allow_merge_if_build_succeeds :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled
) )
end end
......
module AvatarsHelper
def author_avatar(commit_or_event, options = {})
user_avatar(options.merge({
user: commit_or_event.author,
user_name: commit_or_event.author_name,
user_email: commit_or_event.author_email,
}))
end
private
def user_avatar(options = {})
avatar_size = options[:size] || 16
user_name = options[:user].try(:name) || options[:user_name]
avatar = image_tag(
avatar_icon(options[:user] || options[:user_email], avatar_size),
class: "avatar has-tooltip hidden-xs s#{avatar_size}",
alt: "#{user_name}'s avatar",
title: user_name
)
if options[:user]
link_to(avatar, user_path(options[:user]))
elsif options[:user_email]
mail_to(options[:user_email], avatar)
end
end
end
...@@ -15,8 +15,11 @@ module CiStatusHelper ...@@ -15,8 +15,11 @@ module CiStatusHelper
end end
def ci_label_for_status(status) def ci_label_for_status(status)
if status == 'success' case status
when 'success'
'passed' 'passed'
when 'success_with_warnings'
'passed with warnings'
else else
status status
end end
......
...@@ -16,16 +16,6 @@ module CommitsHelper ...@@ -16,16 +16,6 @@ module CommitsHelper
commit_person_link(commit, options.merge(source: :committer)) commit_person_link(commit, options.merge(source: :committer))
end end
def commit_author_avatar(commit, options = {})
options = options.merge(source: :author)
user = commit.send(options[:source])
source_email = clean(commit.send "#{options[:source]}_email".to_sym)
person_email = user.try(:email) || source_email
image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]} hidden-xs", width: options[:size], alt: "")
end
def image_diff_class(diff) def image_diff_class(diff)
if diff.deleted_file if diff.deleted_file
"deleted" "deleted"
......
module ServicesHelper
def service_event_description(event)
case event
when "push"
"Event will be triggered by a push to the repository"
when "tag_push"
"Event will be triggered when a new tag is pushed to the repository"
when "note"
"Event will be triggered when someone adds a comment"
when "issue"
"Event will be triggered when an issue is created/updated/merged"
when "merge_request"
"Event will be triggered when a merge request is created/updated/merged"
when "build"
"Event will be triggered when a build status changes"
when "wiki_page"
"Event will be triggered when a wiki page is created/updated"
end
end
def service_event_field_name(event)
event = event.pluralize if %w[merge_request issue].include?(event)
"#{event}_events"
end
end
module TimeHelper module TimeHelper
def time_interval_in_words(interval_in_seconds) def time_interval_in_words(interval_in_seconds)
interval_in_seconds = interval_in_seconds.to_i
minutes = interval_in_seconds / 60 minutes = interval_in_seconds / 60
seconds = interval_in_seconds - minutes * 60 seconds = interval_in_seconds - minutes * 60
......
...@@ -172,7 +172,7 @@ class Ability ...@@ -172,7 +172,7 @@ class Ability
rules << :read_build if project.public_builds? rules << :read_build if project.public_builds?
unless owner || project.team.member?(user) || project_group_member?(project, user) unless owner || project.team.member?(user) || project_group_member?(project, user)
rules << :request_access rules << :request_access if project.request_access_enabled
end end
end end
...@@ -373,7 +373,7 @@ class Ability ...@@ -373,7 +373,7 @@ class Ability
end end
if group.public? || (group.internal? && !user.external?) if group.public? || (group.internal? && !user.external?)
rules << :request_access unless group.users.include?(user) rules << :request_access if group.request_access_enabled && group.users.exclude?(user)
end end
rules.flatten rules.flatten
......
...@@ -4,12 +4,20 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -4,12 +4,20 @@ class ApplicationSetting < ActiveRecord::Base
add_authentication_token_field :health_check_access_token add_authentication_token_field :health_check_access_token
CACHE_KEY = 'application_setting.last' CACHE_KEY = 'application_setting.last'
DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
| # or
\s # any whitespace character
| # or
[\r\n] # any number of newline characters
}x
serialize :restricted_visibility_levels serialize :restricted_visibility_levels
serialize :import_sources serialize :import_sources
serialize :disabled_oauth_sign_in_sources, Array serialize :disabled_oauth_sign_in_sources, Array
serialize :restricted_signup_domains, Array serialize :domain_whitelist, Array
attr_accessor :restricted_signup_domains_raw serialize :domain_blacklist, Array
attr_accessor :domain_whitelist_raw, :domain_blacklist_raw
validates :session_expire_delay, validates :session_expire_delay,
presence: true, presence: true,
...@@ -62,6 +70,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -62,6 +70,10 @@ class ApplicationSetting < ActiveRecord::Base
validates :enabled_git_access_protocol, validates :enabled_git_access_protocol,
inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true } inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true }
validates :domain_blacklist,
presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' },
if: :domain_blacklist_enabled?
validates_each :restricted_visibility_levels do |record, attr, value| validates_each :restricted_visibility_levels do |record, attr, value|
unless value.nil? unless value.nil?
value.each do |level| value.each do |level|
...@@ -129,7 +141,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -129,7 +141,7 @@ class ApplicationSetting < ActiveRecord::Base
session_expire_delay: Settings.gitlab['session_expire_delay'], session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], domain_whitelist: Settings.gitlab['domain_whitelist'],
import_sources: %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project], import_sources: %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'], max_artifacts_size: Settings.artifacts['max_size'],
...@@ -150,20 +162,30 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -150,20 +162,30 @@ class ApplicationSetting < ActiveRecord::Base
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url) ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end end
def restricted_signup_domains_raw def domain_whitelist_raw
self.restricted_signup_domains.join("\n") unless self.restricted_signup_domains.nil? self.domain_whitelist.join("\n") unless self.domain_whitelist.nil?
end end
def restricted_signup_domains_raw=(values) def domain_blacklist_raw
self.restricted_signup_domains = [] self.domain_blacklist.join("\n") unless self.domain_blacklist.nil?
self.restricted_signup_domains = values.split( end
/\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
| # or def domain_whitelist_raw=(values)
\s # any whitespace character self.domain_whitelist = []
| # or self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR)
[\r\n] # any number of newline characters self.domain_whitelist.reject! { |d| d.empty? }
/x) self.domain_whitelist
self.restricted_signup_domains.reject! { |d| d.empty? } end
def domain_blacklist_raw=(values)
self.domain_blacklist = []
self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR)
self.domain_blacklist.reject! { |d| d.empty? }
self.domain_blacklist
end
def domain_blacklist_file=(file)
self.domain_blacklist_raw = file.read
end end
def runners_registration_token def runners_registration_token
......
...@@ -12,7 +12,7 @@ module Ci ...@@ -12,7 +12,7 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) } scope :unstarted, ->() { where(runner_id: nil) }
scope :ignore_failures, ->() { where(allow_failure: false) } scope :ignore_failures, ->() { where(allow_failure: false) }
scope :with_artifacts, ->() { where.not(artifacts_file: nil) } scope :with_artifacts, ->() { where.not(artifacts_file: [nil, '']) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual) } scope :manual_actions, ->() { where(when: :manual) }
...@@ -97,7 +97,7 @@ module Ci ...@@ -97,7 +97,7 @@ module Ci
end end
def other_actions def other_actions
pipeline.manual_actions.where.not(id: self) pipeline.manual_actions.where.not(name: name)
end end
def playable? def playable?
...@@ -145,7 +145,15 @@ module Ci ...@@ -145,7 +145,15 @@ module Ci
end end
def variables def variables
predefined_variables + yaml_variables + project_variables + trigger_variables variables = predefined_variables
variables += project.predefined_variables
variables += pipeline.predefined_variables
variables += runner.predefined_variables if runner
variables += project.container_registry_variables
variables += yaml_variables
variables += project.secret_variables
variables += trigger_request.user_variables if trigger_request
variables
end end
def merge_request def merge_request
...@@ -430,28 +438,23 @@ module Ci ...@@ -430,28 +438,23 @@ module Ci
self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil) self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
end end
def project_variables
project.variables.map do |variable|
{ key: variable.key, value: variable.value, public: false }
end
end
def trigger_variables
if trigger_request && trigger_request.variables
trigger_request.variables.map do |key, value|
{ key: key, value: value, public: false }
end
else
[]
end
end
def predefined_variables def predefined_variables
variables = [] variables = [
variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag? { key: 'CI', value: 'true', public: true },
variables << { key: :CI_BUILD_NAME, value: name, public: true } { key: 'GITLAB_CI', value: 'true', public: true },
variables << { key: :CI_BUILD_STAGE, value: stage, public: true } { key: 'CI_BUILD_ID', value: id.to_s, public: true },
variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request { key: 'CI_BUILD_TOKEN', value: token, public: false },
{ key: 'CI_BUILD_REF', value: sha, public: true },
{ key: 'CI_BUILD_BEFORE_SHA', value: before_sha, public: true },
{ key: 'CI_BUILD_REF_NAME', value: ref, public: true },
{ key: 'CI_BUILD_NAME', value: name, public: true },
{ key: 'CI_BUILD_STAGE', value: stage, public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true }
]
variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag?
variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request
variables variables
end end
......
...@@ -20,6 +20,11 @@ module Ci ...@@ -20,6 +20,11 @@ module Ci
after_touch :update_state after_touch :update_state
after_save :keep_around_commits after_save :keep_around_commits
# ref can't be HEAD or SHA, can only be branch/tag name
scope :latest_successful_for, ->(ref = default_branch) do
where(ref: ref).success.order(id: :desc).limit(1)
end
def self.truncate_sha(sha) def self.truncate_sha(sha)
sha[0...8] sha[0...8]
end end
...@@ -146,6 +151,10 @@ module Ci ...@@ -146,6 +151,10 @@ module Ci
end end
end end
def has_warnings?
builds.latest.ignored.any?
end
def config_processor def config_processor
return nil unless ci_yaml_file return nil unless ci_yaml_file
return @config_processor if defined?(@config_processor) return @config_processor if defined?(@config_processor)
...@@ -198,6 +207,12 @@ module Ci ...@@ -198,6 +207,12 @@ module Ci
Note.for_commit_id(sha) Note.for_commit_id(sha)
end end
def predefined_variables
[
{ key: 'CI_PIPELINE_ID', value: id.to_s, public: true }
]
end
private private
def build_builds_for_stages(stages, user, status, trigger_request) def build_builds_for_stages(stages, user, status, trigger_request)
...@@ -206,8 +221,9 @@ module Ci ...@@ -206,8 +221,9 @@ module Ci
# build builds only for the first stage that has builds available. # build builds only for the first stage that has builds available.
# #
stages.any? do |stage| stages.any? do |stage|
CreateBuildsService.new(self) CreateBuildsService.new(self).
.execute(stage, user, status, trigger_request).present? execute(stage, user, status, trigger_request).
any?(&:active?)
end end
end end
......
...@@ -114,6 +114,14 @@ module Ci ...@@ -114,6 +114,14 @@ module Ci
tag_list.any? tag_list.any?
end end
def predefined_variables
[
{ key: 'CI_RUNNER_ID', value: id.to_s, public: true },
{ key: 'CI_RUNNER_DESCRIPTION', value: description, public: true },
{ key: 'CI_RUNNER_TAGS', value: tag_list.to_s, public: true }
]
end
private private
def tag_constraints def tag_constraints
......
...@@ -7,5 +7,13 @@ module Ci ...@@ -7,5 +7,13 @@ module Ci
has_many :builds, class_name: 'Ci::Build' has_many :builds, class_name: 'Ci::Build'
serialize :variables serialize :variables
def user_variables
return [] unless variables
variables.map do |key, value|
{ key: key, value: value, public: false }
end
end
end end
end end
...@@ -16,7 +16,11 @@ class CommitStatus < ActiveRecord::Base ...@@ -16,7 +16,11 @@ class CommitStatus < ActiveRecord::Base
alias_attribute :author, :user alias_attribute :author, :user
scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :commit_id)) } scope :latest, -> do
max_id = unscope(:select).select("max(#{quoted_table_name}.id)")
where(id: max_id.group(:name, :commit_id))
end
scope :retried, -> { where.not(id: latest) } scope :retried, -> { where.not(id: latest) }
scope :ordered, -> { order(:name) } scope :ordered, -> { order(:name) }
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) } scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
......
...@@ -52,10 +52,50 @@ class Issue < ActiveRecord::Base ...@@ -52,10 +52,50 @@ class Issue < ActiveRecord::Base
attributes attributes
end end
class << self
private
# Returns the project that the current scope belongs to if any, nil otherwise.
#
# Examples:
# - my_project.issues.without_due_date.owner_project => my_project
# - Issue.all.owner_project => nil
def owner_project
# No owner if we're not being called from an association
return unless all.respond_to?(:proxy_association)
owner = all.proxy_association.owner
# Check if the association is or belongs to a project
if owner.is_a?(Project)
owner
else
begin
owner.association(:project).target
rescue ActiveRecord::AssociationNotFoundError
nil
end
end
end
end
def self.visible_to_user(user) def self.visible_to_user(user)
return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank? return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
return all if user.admin? return all if user.admin?
# Check if we are scoped to a specific project's issues
if owner_project
if owner_project.authorized_for_user?(user, Gitlab::Access::REPORTER)
# If the project is authorized for the user, they can see all issues in the project
return all
else
# else only non confidential and authored/assigned to them
return where('issues.confidential IS NULL OR issues.confidential IS FALSE
OR issues.author_id = :user_id OR issues.assignee_id = :user_id',
user_id: user.id)
end
end
where(' where('
issues.confidential IS NULL issues.confidential IS NULL
OR issues.confidential IS FALSE OR issues.confidential IS FALSE
......
...@@ -429,6 +429,17 @@ class Project < ActiveRecord::Base ...@@ -429,6 +429,17 @@ class Project < ActiveRecord::Base
repository.commit(ref) repository.commit(ref)
end end
# ref can't be HEAD, can only be branch/tag name or SHA
def latest_successful_builds_for(ref = default_branch)
pipeline = pipelines.latest_successful_for(ref).to_sql
join_sql = "INNER JOIN (#{pipeline}) pipelines" +
" ON pipelines.id = #{Ci::Build.quoted_table_name}.commit_id"
builds.joins(join_sql).latest.with_artifacts
# TODO: Whenever we dropped support for MySQL, we could change to:
# pipeline = pipelines.latest_successful_for(ref)
# builds.where(pipeline: pipeline).latest.with_artifacts
end
def merge_base_commit(first_commit_id, second_commit_id) def merge_base_commit(first_commit_id, second_commit_id)
sha = repository.merge_base(first_commit_id, second_commit_id) sha = repository.merge_base(first_commit_id, second_commit_id)
repository.commit(sha) if sha repository.commit(sha) if sha
...@@ -1180,4 +1191,74 @@ class Project < ActiveRecord::Base ...@@ -1180,4 +1191,74 @@ class Project < ActiveRecord::Base
def ensure_dir_exist def ensure_dir_exist
gitlab_shell.add_namespace(repository_storage_path, namespace.path) gitlab_shell.add_namespace(repository_storage_path, namespace.path)
end end
def predefined_variables
[
{ key: 'CI_PROJECT_ID', value: id.to_s, public: true },
{ key: 'CI_PROJECT_NAME', value: path, public: true },
{ key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true },
{ key: 'CI_PROJECT_NAMESPACE', value: namespace.path, public: true },
{ key: 'CI_PROJECT_URL', value: web_url, public: true }
]
end
def container_registry_variables
return [] unless Gitlab.config.registry.enabled
variables = [
{ key: 'CI_REGISTRY', value: Gitlab.config.registry.host_port, public: true }
]
if container_registry_enabled?
variables << { key: 'CI_REGISTRY_IMAGE', value: container_registry_repository_url, public: true }
end
variables
end
def secret_variables
variables.map do |variable|
{ key: variable.key, value: variable.value, public: false }
end
end
# Checks if `user` is authorized for this project, with at least the
# `min_access_level` (if given).
#
# If you change the logic of this method, please also update `User#authorized_projects`
def authorized_for_user?(user, min_access_level = nil)
return false unless user
return true if personal? && namespace_id == user.namespace_id
authorized_for_user_by_group?(user, min_access_level) ||
authorized_for_user_by_members?(user, min_access_level) ||
authorized_for_user_by_shared_projects?(user, min_access_level)
end
private
def authorized_for_user_by_group?(user, min_access_level)
member = user.group_members.find_by(source_id: group)
member && (!min_access_level || member.access_level >= min_access_level)
end
def authorized_for_user_by_members?(user, min_access_level)
member = members.find_by(user_id: user)
member && (!min_access_level || member.access_level >= min_access_level)
end
def authorized_for_user_by_shared_projects?(user, min_access_level)
shared_projects = user.group_members.joins(group: :shared_projects).
where(project_group_links: { project_id: self })
if min_access_level
members_scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } }
shared_projects = shared_projects.where(members: members_scope)
end
shared_projects.any?
end
end end
...@@ -4,6 +4,9 @@ class SlackService < Service ...@@ -4,6 +4,9 @@ class SlackService < Service
validates :webhook, presence: true, url: true, if: :activated? validates :webhook, presence: true, url: true, if: :activated?
def initialize_properties def initialize_properties
# Custom serialized properties initialization
self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) }
if properties.nil? if properties.nil?
self.properties = {} self.properties = {}
self.notify_only_broken_builds = true self.notify_only_broken_builds = true
...@@ -29,13 +32,15 @@ class SlackService < Service ...@@ -29,13 +32,15 @@ class SlackService < Service
end end
def fields def fields
default_fields =
[ [
{ type: 'text', name: 'webhook', { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' },
placeholder: 'https://hooks.slack.com/services/...' },
{ type: 'text', name: 'username', placeholder: 'username' }, { type: 'text', name: 'username', placeholder: 'username' },
{ type: 'text', name: 'channel', placeholder: '#channel' }, { type: 'text', name: 'channel', placeholder: "#general" },
{ type: 'checkbox', name: 'notify_only_broken_builds' }, { type: 'checkbox', name: 'notify_only_broken_builds' },
] ]
default_fields + build_event_channels
end end
def supported_events def supported_events
...@@ -74,7 +79,10 @@ class SlackService < Service ...@@ -74,7 +79,10 @@ class SlackService < Service
end end
opt = {} opt = {}
opt[:channel] = channel if channel
event_channel = get_channel_field(object_kind) || channel
opt[:channel] = event_channel if event_channel
opt[:username] = username if username opt[:username] = username if username
if message if message
...@@ -83,8 +91,35 @@ class SlackService < Service ...@@ -83,8 +91,35 @@ class SlackService < Service
end end
end end
def event_channel_names
supported_events.map { |event| event_channel_name(event) }
end
def event_field(event)
fields.find { |field| field[:name] == event_channel_name(event) }
end
def global_fields
fields.reject { |field| field[:name].end_with?('channel') }
end
private private
def get_channel_field(event)
field_name = event_channel_name(event)
self.public_send(field_name)
end
def build_event_channels
supported_events.reduce([]) do |channels, event|
channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" }
end
end
def event_channel_name(event)
"#{event}_channel"
end
def project_name def project_name
project.name_with_namespace.gsub(/\s/, '') project.name_with_namespace.gsub(/\s/, '')
end end
......
...@@ -26,7 +26,7 @@ class Service < ActiveRecord::Base ...@@ -26,7 +26,7 @@ class Service < ActiveRecord::Base
scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) } scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) }
scope :issue_trackers, -> { where(category: 'issue_tracker') } scope :issue_trackers, -> { where(category: 'issue_tracker') }
scope :external_wikis, -> { where(type: 'ExternalWikiService') } scope :external_wikis, -> { where(type: 'ExternalWikiService').active }
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
scope :without_defaults, -> { where(default: false) } scope :without_defaults, -> { where(default: false) }
...@@ -82,6 +82,18 @@ class Service < ActiveRecord::Base ...@@ -82,6 +82,18 @@ class Service < ActiveRecord::Base
Gitlab::PushDataBuilder.build_sample(project, user) Gitlab::PushDataBuilder.build_sample(project, user)
end end
def event_channel_names
[]
end
def event_field(event)
nil
end
def global_fields
fields
end
def supported_events def supported_events
%w(push tag_push issue merge_request wiki_page) %w(push tag_push issue merge_request wiki_page)
end end
......
...@@ -111,7 +111,7 @@ class User < ActiveRecord::Base ...@@ -111,7 +111,7 @@ class User < ActiveRecord::Base
validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
before_validation :generate_password, on: :create before_validation :generate_password, on: :create
before_validation :restricted_signup_domains, on: :create before_validation :signup_domain_valid?, on: :create
before_validation :sanitize_attrs before_validation :sanitize_attrs
before_validation :set_notification_email, if: ->(user) { user.email_changed? } before_validation :set_notification_email, if: ->(user) { user.email_changed? }
before_validation :set_public_email, if: ->(user) { user.public_email_changed? } before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
...@@ -412,6 +412,8 @@ class User < ActiveRecord::Base ...@@ -412,6 +412,8 @@ class User < ActiveRecord::Base
end end
# Returns projects user is authorized to access. # Returns projects user is authorized to access.
#
# If you change the logic of this method, please also update `Project#authorized_for_user`
def authorized_projects(min_access_level = nil) def authorized_projects(min_access_level = nil)
Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})") Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})")
end end
...@@ -760,29 +762,6 @@ class User < ActiveRecord::Base ...@@ -760,29 +762,6 @@ class User < ActiveRecord::Base
Project.where(id: events) Project.where(id: events)
end end
def restricted_signup_domains
email_domains = current_application_settings.restricted_signup_domains
unless email_domains.blank?
match_found = email_domains.any? do |domain|
escaped = Regexp.escape(domain).gsub('\*', '.*?')
regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
email_domain = Mail::Address.new(self.email).domain
email_domain =~ regexp
end
unless match_found
self.errors.add :email,
'is not whitelisted. ' +
'Email domains valid for registration are: ' +
email_domains.join(', ')
return false
end
end
true
end
def can_be_removed? def can_be_removed?
!solo_owned_groups.present? !solo_owned_groups.present?
end end
...@@ -881,4 +860,40 @@ class User < ActiveRecord::Base ...@@ -881,4 +860,40 @@ class User < ActiveRecord::Base
self.can_create_group = false self.can_create_group = false
self.projects_limit = 0 self.projects_limit = 0
end end
def signup_domain_valid?
valid = true
error = nil
if current_application_settings.domain_blacklist_enabled?
blocked_domains = current_application_settings.domain_blacklist
if domain_matches?(blocked_domains, self.email)
error = 'is not from an allowed domain.'
valid = false
end
end
allowed_domains = current_application_settings.domain_whitelist
unless allowed_domains.blank?
if domain_matches?(allowed_domains, self.email)
valid = true
else
error = "is not whitelisted. Email domains valid for registration are: #{allowed_domains.join(', ')}"
valid = false
end
end
self.errors.add(:email, error) unless valid
valid
end
def domain_matches?(email_domains, email)
signup_domain = Mail::Address.new(email).domain
email_domains.any? do |domain|
escaped = Regexp.escape(domain).gsub('\*', '.*?')
regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE
signup_domain =~ regexp
end
end
end end
...@@ -33,16 +33,15 @@ class FileUploader < CarrierWave::Uploader::Base ...@@ -33,16 +33,15 @@ class FileUploader < CarrierWave::Uploader::Base
end end
def to_h def to_h
filename = image? ? self.file.basename : self.file.filename filename = image_or_video? ? self.file.basename : self.file.filename
escaped_filename = filename.gsub("]", "\\]") escaped_filename = filename.gsub("]", "\\]")
markdown = "[#{escaped_filename}](#{self.secure_url})" markdown = "[#{escaped_filename}](#{self.secure_url})"
markdown.prepend("!") if image? markdown.prepend("!") if image_or_video?
{ {
alt: filename, alt: filename,
url: self.secure_url, url: self.secure_url,
is_image: image?,
markdown: markdown markdown: markdown
} }
end end
......
# Extra methods for uploader # Extra methods for uploader
module UploaderHelper module UploaderHelper
IMAGE_EXT = %w[png jpg jpeg gif bmp tiff]
# We recommend using the .mp4 format over .mov. Videos in .mov format can
# still be used but you really need to make sure they are served with the
# proper MIME type video/mp4 and not video/quicktime or your videos won't play
# on IE >= 9.
# http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html
VIDEO_EXT = %w[mp4 m4v mov webm ogv]
def image? def image?
img_ext = %w(png jpg jpeg gif bmp tiff) extension_match?(IMAGE_EXT)
end
def video?
extension_match?(VIDEO_EXT)
end
def image_or_video?
image? || video?
end
def extension_match?(extensions)
return false unless file
extension =
if file.respond_to?(:extension) if file.respond_to?(:extension)
img_ext.include?(file.extension.downcase) file.extension
else else
# Not all CarrierWave storages respond to :extension # Not all CarrierWave storages respond to :extension
ext = file.path.split('.').last.downcase File.extname(file.path).delete('.')
img_ext.include?(ext)
end end
rescue
false extensions.include?(extension.downcase)
end end
def file_storage? def file_storage?
......
...@@ -109,7 +109,7 @@ ...@@ -109,7 +109,7 @@
Newly registered users will by default be external Newly registered users will by default be external
%fieldset %fieldset
%legend Sign-in Restrictions %legend Sign-up Restrictions
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
...@@ -122,6 +122,49 @@ ...@@ -122,6 +122,49 @@
= f.label :send_user_confirmation_email do = f.label :send_user_confirmation_email do
= f.check_box :send_user_confirmation_email = f.check_box :send_user_confirmation_email
Send confirmation email on sign-up Send confirmation email on sign-up
.form-group
= f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.help-block ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'control-label col-sm-2'
.col-sm-10
.checkbox
= f.label :domain_blacklist_enabled do
= f.check_box :domain_blacklist_enabled
Enable domain blacklist for sign ups
.form-group
.col-sm-offset-2.col-sm-10
.radio
= label_tag :blacklist_type_file do
= radio_button_tag :blacklist_type, :file
.option-title
Upload blacklist file
.radio
= label_tag :blacklist_type_raw do
= radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank?
.option-title
Enter blacklist manually
.form-group.blacklist-file
= f.label :domain_blacklist_file, 'Blacklist file', class: 'control-label col-sm-2'
.col-sm-10
= f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf'
.help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries.
.form-group.blacklist-raw
= f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
.help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group
= f.label :after_sign_up_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :after_sign_up_text, class: 'form-control', rows: 4
.help-block Markdown enabled
%fieldset
%legend Sign-in Restrictions
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
...@@ -147,11 +190,6 @@ ...@@ -147,11 +190,6 @@
.col-sm-10 .col-sm-10
= f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0' = f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
.help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication .help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
.form-group
= f.label :restricted_signup_domains, 'Restricted domains for sign-ups', class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :restricted_signup_domains_raw, placeholder: 'domain.com', class: 'form-control'
.help-block Only users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
.form-group .form-group
= f.label :home_page_url, 'Home page URL', class: 'control-label col-sm-2' = f.label :home_page_url, 'Home page URL', class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
...@@ -167,11 +205,6 @@ ...@@ -167,11 +205,6 @@
.col-sm-10 .col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4 = f.text_area :sign_in_text, class: 'form-control', rows: 4
.help-block Markdown enabled .help-block Markdown enabled
.form-group
= f.label :after_sign_up_text, class: 'control-label col-sm-2'
.col-sm-10
= f.text_area :after_sign_up_text, class: 'form-control', rows: 4
.help-block Markdown enabled
.form-group .form-group
= f.label :help_page_text, class: 'control-label col-sm-2' = f.label :help_page_text, class: 'control-label col-sm-2'
.col-sm-10 .col-sm-10
......
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
.form-group
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
- if @group.new_record? - if @group.new_record?
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
......
...@@ -4,11 +4,7 @@ ...@@ -4,11 +4,7 @@
#{time_ago_with_tooltip(event.created_at)} #{time_ago_with_tooltip(event.created_at)}
= cache [event, current_application_settings, "v2.2"] do = cache [event, current_application_settings, "v2.2"] do
- if event.author = author_avatar(event, size: 40)
= link_to user_path(event.author) do
= image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
- else
= image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
- if event.created_project? - if event.created_project?
= render "events/event/created_project", event: event = render "events/event/created_project", event: event
......
%span.event-scope
= event_preposition(event)
- if event.project
= link_to_project event.project
- else
= event.project_name
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
%span.event_label{class: event.action_name} %span{class: event.action_name}
- if event.target - if event.target
= event.action_name = event.action_name
%strong %strong
...@@ -10,12 +10,7 @@ ...@@ -10,12 +10,7 @@
- else - else
= event_action_name(event) = event_action_name(event)
= event_preposition(event) = render "events/event_scope", event: event
- if event.project
= link_to_project event.project
- else
= event.project_name
- if event.target.respond_to?(:title) - if event.target.respond_to?(:title)
.event-body .event-body
......
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
%span.event_label{class: event.action_name} %span{class: event.action_name}
= event_action_name(event) = event_action_name(event)
- if event.project - if event.project
......
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
%span.event_label
= event.action_name = event.action_name
= event_note_title_html(event) = event_note_title_html(event)
at
- if event.project = render "events/event_scope", event: event
= link_to_project event.project
- else
= event.project_name
.event-body .event-body
.event-note .event-note
......
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
.event-title .event-title
%span.author_name= link_to_author event %span.author_name= link_to_author event
%span.event_label.pushed #{event.action_name} #{event.ref_type} %span.pushed #{event.action_name} #{event.ref_type}
- if event.rm_ref? - if event.rm_ref?
%strong= event.ref_name %strong= event.ref_name
- else - else
%strong %strong
= link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title) = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title)
at
= link_to_project project = render "events/event_scope", event: event
- if event.push_with_commits? - if event.push_with_commits?
.event-body .event-body
......
...@@ -21,6 +21,10 @@ ...@@ -21,6 +21,10 @@
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
.form-group
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
.form-group .form-group
%hr %hr
= f.label :share_with_group_lock, class: 'control-label' do = f.label :share_with_group_lock, class: 'control-label' do
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do = link_to explore_root_path, title: 'Projects' do
= icon('bookmark fw')
%span %span
Projects Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to explore_groups_path, title: 'Groups' do = link_to explore_groups_path, title: 'Groups' do
= icon('group fw')
%span %span
Groups Groups
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to explore_snippets_path, title: 'Snippets' do = link_to explore_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
%span %span
Snippets Snippets
= nav_link(controller: :help) do = nav_link(controller: :help) do
= link_to help_path, title: 'Help' do = link_to help_path, title: 'Help' do
= icon('question-circle fw')
%span %span
Help Help
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
= link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
%span %span
Triggers Triggers
= nav_link(controller: :badges) do = nav_link(controller: :pipelines_settings) do
= link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do = link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
%span %span
Badges CI/CD Pipelines
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/cropper.js') = page_specific_javascript_tag('lib/cropper.js')
= page_specific_javascript_tag('profile/application.js') = page_specific_javascript_tag('profile/profile_bundle.js')
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
%i.fa.fa-rss %i.fa.fa-rss
= render 'shared/event_filter' = render 'shared/event_filter'
.content_list{:"data-href" => activity_project_path(@project)}
.content_list.project-activity{:"data-href" => activity_project_path(@project)}
= spinner = spinner
:javascript :javascript
......
%fieldset.builds-feature
%h5.prepend-top-0
Builds
- unless @repository.gitlab_ci_yml
.form-group
%p Builds need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
.form-group
%p Get recent application code using the following command:
.radio
= f.label :build_allow_git_fetch_false do
= f.radio_button :build_allow_git_fetch, 'false'
%strong git clone
%br
%span.descr Slower but makes sure you have a clean dir before every build
.radio
= f.label :build_allow_git_fetch_true do
= f.radio_button :build_allow_git_fetch, 'true'
%strong git fetch
%br
%span.descr Faster
.form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
= f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
%p.help-block per build in minutes
.form-group
= f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
.input-group
%span.input-group-addon /
= f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
%span.input-group-addon /
%p.help-block
We will use this regular expression to find test coverage output in build trace.
Leave blank if you want to disable this feature
.bs-callout.bs-callout-info
%p Below are examples of regex for existing tools:
%ul
%li
Simplecov (Ruby) -
%code \(\d+.\d+\%\) covered
%li
pytest-cov (Python) -
%code \d+\%\s*$
%li
phpunit --coverage-text --colors=never (PHP) -
%code ^\s*Lines:\s*\d+.\d+\%
%li
gcovr (C/C++) -
%code ^TOTAL.*\s+(\d+\%)$
%li
tap --coverage-report=text-summary (Node.js) -
%code ^Statements\s*:\s*([^%]+)
.form-group
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public builds
.help-block Allow everyone to access builds for Public and Internal projects
.form-group.append-bottom-0
= f.label :runners_token, "Runners token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used to checkout project.
- page_title 'Badges'
- badges_path = namespace_project_badges_path(@project.namespace, @project)
.prepend-top-10
.panel.panel-default
.panel-heading
%b Builds badge &middot;
= @build_badge.to_html
.pull-right
= render 'shared/ref_switcher', destination: 'badges', align_right: true
.panel-body
.row
.col-md-2.text-center
Markdown
.col-md-10.code.js-syntax-highlight
= highlight('.md', @build_badge.to_markdown)
.row
%hr
.row
.col-md-2.text-center
HTML
.col-md-10.code.js-syntax-highlight
= highlight('.html', @build_badge.to_html)
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
%p.commit-title %p.commit-title
- if commit = pipeline.commit - if commit = pipeline.commit
= commit_author_avatar(commit, size: 20) = author_avatar(commit, size: 20)
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message" = link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
- else - else
Cant find HEAD commit for this branch Cant find HEAD commit for this branch
......
...@@ -9,7 +9,8 @@ ...@@ -9,7 +9,8 @@
= cache(cache_key) do = cache(cache_key) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" } %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
= commit_author_avatar(commit, size: 36) = author_avatar(commit, size: 36)
.commit-info-block .commit-info-block
.commit-row-title .commit-row-title
%span.item-title %span.item-title
......
...@@ -17,6 +17,6 @@ ...@@ -17,6 +17,6 @@
- if local_assigns.fetch(:allow_rollback, false) - if local_assigns.fetch(:allow_rollback, false)
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do
- if deployment.last? - if deployment.last?
Retry Re-deploy
- else - else
Rollback Rollback
...@@ -32,6 +32,10 @@ ...@@ -32,6 +32,10 @@
%strong %strong
= visibility_level_label(@project.visibility_level) = visibility_level_label(@project.visibility_level)
.light= visibility_level_description(@project.visibility_level, @project) .light= visibility_level_description(@project.visibility_level, @project)
.form-group
= render 'shared/allow_request_access', form: f
.form-group .form-group
= f.label :tag_list, "Tags", class: 'label-light' = f.label :tag_list, "Tags", class: 'label-light'
= f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
...@@ -86,8 +90,6 @@ ...@@ -86,8 +90,6 @@
%hr %hr
= render 'merge_request_settings', f: f = render 'merge_request_settings', f: f
%hr %hr
= render 'builds_settings', f: f
%hr
%fieldset.features.append-bottom-default %fieldset.features.append-bottom-default
%h5.prepend-top-0 %h5.prepend-top-0
Project avatar Project avatar
......
- page_title "Fork project" - page_title "Fork project"
- if @namespaces.present?
%h3.page-title Fork project
%p.lead
Click to fork the project to a user or group
%hr
.row.prepend-top-default
.col-lg-3
%h4.prepend-top-0
Fork project
%p
A fork is a copy of a project.
%br
Forking a repository allows you to make changes without affecting the original project.
.col-lg-9
.fork-namespaces .fork-namespaces
- if @namespaces.present?
%label.label-light
%span
Click to fork the project to a user or group
- @namespaces.in_groups_of(6, false) do |group| - @namespaces.in_groups_of(6, false) do |group|
.row .row
- group.each do |namespace| - group.each do |namespace|
.col-md-2.col-sm-3 - avatar = namespace_icon(namespace, 100)
- if fork = namespace.find_fork_of(@project) - if fork = namespace.find_fork_of(@project)
.fork-thumbnail .fork-thumbnail.forked
= link_to project_path(fork), title: "Visit project fork", class: 'has-tooltip' do = link_to project_path(fork) do
= image_tag namespace_icon(namespace, 100) - if /no_((\w*)_)*avatar/.match(avatar)
.no-avatar
= icon 'question'
- else
= image_tag avatar
.caption .caption
%strong
= namespace.human_name = namespace.human_name
%div.text-primary
Already forked
- else - else
.fork-thumbnail .fork-thumbnail
= link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has-tooltip' do = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), method: "POST" do
= image_tag namespace_icon(namespace, 100) - if /no_((\w*)_)*avatar/.match(avatar)
.no-avatar
= icon 'question'
- else
= image_tag avatar
.caption .caption
%strong
= namespace.human_name = namespace.human_name
- else
%p.light %label.label-light
Fork is a copy of a project repository. %span
No available namespaces to fork the project.
%br %br
Forking a repository allows you to do changes without affecting the original project. %small
- else
%h3 No available namespaces to fork the project
%p.slead
You must have permission to create a project in a namespace before forking. You must have permission to create a project in a namespace before forking.
.save-project-loader.hide .save-project-loader.hide
.center .center
%h2 %h2
%i.fa.fa-spinner.fa-spin %i.fa.fa-spinner.fa-spin
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/chart.js') = page_specific_javascript_tag('lib/chart.js')
= page_specific_javascript_tag('graphs/application.js') = page_specific_javascript_tag('graphs/graphs_bundle.js')
= nav_link(action: :show) do = nav_link(action: :show) do
= link_to 'Contributors', namespace_project_graph_path = link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do = nav_link(action: :commits) do
......
- if @pipeline - if @pipeline
.mr-widget-heading .mr-widget-heading
- %w[success skipped canceled failed running pending].each do |status| - %w[success success_with_warnings skipped canceled failed running pending].each do |status|
.ci_widget{ class: "ci-#{status}", style: ("display:none" unless @pipeline.status == status) } .ci_widget{ class: "ci-#{status}", style: ("display:none" unless @pipeline.status == status) }
= ci_icon_for_status(status) = ci_icon_for_status(status)
%span %span
......
- page_title "Network", @ref - page_title "Network", @ref
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/raphael.js') = page_specific_javascript_tag('lib/raphael.js')
= page_specific_javascript_tag('network/application.js') = page_specific_javascript_tag('network/network_bundle.js')
= render "projects/commits/head" = render "projects/commits/head"
= render "head" = render "head"
%div{ class: container_class } %div{ class: container_class }
......
...@@ -89,9 +89,9 @@ ...@@ -89,9 +89,9 @@
= link_to "#", class: 'btn js-toggle-button import_git' do = link_to "#", class: 'btn js-toggle-button import_git' do
%i.fa.fa-git %i.fa.fa-git
%span Repo by URL %span Repo by URL
%div %div{ class: 'import_gitlab_project' }
- if gitlab_project_import_enabled? - if gitlab_project_import_enabled?
= link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do
%i.fa.fa-gitlab %i.fa.fa-gitlab
%span GitLab export %span GitLab export
...@@ -130,29 +130,29 @@ ...@@ -130,29 +130,29 @@
$(".modal").hide(); $(".modal").hide();
}); });
$('.import_gitlab_project').bind('click', function() { $('.btn_import_gitlab_project').bind('click', function() {
var _href = $("a.import_gitlab_project").attr("href"); var _href = $("a.btn_import_gitlab_project").attr("href");
$(".import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val()); $(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val());
}); });
$('.import_gitlab_project').attr('disabled',true) $('.btn_import_gitlab_project').attr('disabled',true)
$('.import_gitlab_project').attr('title', 'Project path required.'); $('.import_gitlab_project').attr('title', 'Project path and name required.');
$('.import_gitlab_project').click(function( event ) { $('.import_gitlab_project').click(function( event ) {
if($('.import_gitlab_project').attr('disabled')) { if($('.btn_import_gitlab_project').attr('disabled')) {
event.preventDefault(); event.preventDefault();
new Flash("Please enter a path for the project to be imported to."); new Flash("Please enter path and name for the project to be imported to.");
} }
}); });
$('#project_path').keyup(function(){ $('#project_path').keyup(function(){
if($(this).val().length !=0) { if($(this).val().length !=0) {
$('.import_gitlab_project').attr('disabled', false); $('.btn_import_gitlab_project').attr('disabled', false);
$('.import_gitlab_project').attr('title',''); $('.import_gitlab_project').attr('title','');
$(".flash-container").html("") $(".flash-container").html("")
} else { } else {
$('.import_gitlab_project').attr('disabled',true); $('.btn_import_gitlab_project').attr('disabled',true);
$('.import_gitlab_project').attr('title', 'Project path required.'); $('.import_gitlab_project').attr('title', 'Project path and name required.');
} }
}); });
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
= link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do
= icon('trash-o') = icon('trash-o')
.note-body{class: note_editable ? 'js-task-list-container' : ''} .note-body{class: note_editable ? 'js-task-list-container' : ''}
.note-text .note-text.md
= preserve do = preserve do
= note.note_html = note.note_html
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
......
- page_title "CI/CD Pipelines"
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
= page_title
.col-lg-9
%h5.prepend-top-0
Pipelines
= form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project), remote: true, authenticity_token: true do |f|
%fieldset.builds-feature
- unless @repository.gitlab_ci_yml
.form-group
%p Pipelines need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
.form-group
%p Get recent application code using the following command:
.radio
= f.label :build_allow_git_fetch_false do
= f.radio_button :build_allow_git_fetch, 'false'
%strong git clone
%br
%span.descr Slower but makes sure you have a clean dir before every build
.radio
= f.label :build_allow_git_fetch_true do
= f.radio_button :build_allow_git_fetch, 'true'
%strong git fetch
%br
%span.descr Faster
.form-group
= f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
= f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
%p.help-block per build in minutes
.form-group
= f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
.input-group
%span.input-group-addon /
= f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
%span.input-group-addon /
%p.help-block
We will use this regular expression to find test coverage output in build trace.
Leave blank if you want to disable this feature
.bs-callout.bs-callout-info
%p Below are examples of regex for existing tools:
%ul
%li
Simplecov (Ruby) -
%code \(\d+.\d+\%\) covered
%li
pytest-cov (Python) -
%code \d+\%\s*$
%li
phpunit --coverage-text --colors=never (PHP) -
%code ^\s*Lines:\s*\d+.\d+\%
%li
gcovr (C/C++) -
%code ^TOTAL.*\s+(\d+\%)$
%li
tap --coverage-report=text-summary (Node.js) -
%code ^Statements\s*:\s*([^%]+)
.form-group
.checkbox
= f.label :public_builds do
= f.check_box :public_builds
%strong Public pipelines
.help-block Allow everyone to access pipelines for Public and Internal projects
.form-group.append-bottom-default
= f.label :runners_token, "Runners token", class: 'label-light'
= f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
%p.help-block The secure token used to checkout project.
= f.submit 'Save changes', class: "btn btn-save"
%hr
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
Builds Badge
.col-lg-9
.prepend-top-10
.panel.panel-default
.panel-heading
%b Builds badge &middot;
= @build_badge.to_html
.pull-right
= render 'shared/ref_switcher', destination: 'badges', align_right: true
.panel-body
.row
.col-md-2.text-center
Markdown
.col-md-10.code.js-syntax-highlight
= highlight('.md', @build_badge.to_markdown)
.row
%hr
.row
.col-md-2.text-center
HTML
.col-md-10.code.js-syntax-highlight
= highlight('.html', @build_badge.to_html)
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
.col-lg-9 .col-lg-9
= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form|
= render 'shared/service_settings', form: form = render 'shared/service_settings', form: form
= form.submit 'Save changes', class: 'btn btn-save' = form.submit 'Save changes', class: 'btn btn-save'
&nbsp; &nbsp;
- if @service.valid? && @service.activated? - if @service.valid? && @service.activated?
......
.checkbox
= form.label :request_access_enabled do
= form.check_box :request_access_enabled
%strong Allow users to request access
%br
%span.descr Allow users to request access if visibility is public or internal.
...@@ -10,69 +10,28 @@ ...@@ -10,69 +10,28 @@
.col-sm-10 .col-sm-10
= form.check_box :active = form.check_box :active
- if @service.supported_events.length > 1 .form-group
.form-group
= form.label :url, "Trigger", class: 'control-label' = form.label :url, "Trigger", class: 'control-label'
.col-sm-10 .col-sm-10
- if @service.supported_events.include?("push") - @service.supported_events.each do |event|
%div
= form.check_box :push_events, class: 'pull-left'
.prepend-left-20
= form.label :push_events, class: 'list-label' do
%strong Push events
%p.light
This url will be triggered by a push to the repository
- if @service.supported_events.include?("tag_push")
%div
= form.check_box :tag_push_events, class: 'pull-left'
.prepend-left-20
= form.label :tag_push_events, class: 'list-label' do
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
- if @service.supported_events.include?("note")
%div
= form.check_box :note_events, class: 'pull-left'
.prepend-left-20
= form.label :note_events, class: 'list-label' do
%strong Comments
%p.light
This url will be triggered when someone adds a comment
- if @service.supported_events.include?("issue")
%div
= form.check_box :issues_events, class: 'pull-left'
.prepend-left-20
= form.label :issues_events, class: 'list-label' do
%strong Issues events
%p.light
This url will be triggered when an issue is created/updated/merged
- if @service.supported_events.include?("merge_request")
%div
= form.check_box :merge_requests_events, class: 'pull-left'
.prepend-left-20
= form.label :merge_requests_events, class: 'list-label' do
%strong Merge Request events
%p.light
This url will be triggered when a merge request is created/updated/merged
- if @service.supported_events.include?("build")
%div
= form.check_box :build_events, class: 'pull-left'
.prepend-left-20
= form.label :build_events, class: 'list-label' do
%strong Build events
%p.light
This url will be triggered when a build status changes
- if @service.supported_events.include?("wiki_page")
%div %div
= form.check_box :wiki_page_events, class: 'pull-left' = form.check_box service_event_field_name(event), class: 'pull-left'
.prepend-left-20 .prepend-left-20
= form.label :wiki_page_events, class: 'list-label' do = form.label service_event_field_name(event), class: 'list-label' do
%strong Wiki Page events %strong
%p.light = event.humanize
This url will be triggered when a wiki page is created/updated
- field = @service.event_field(event)
- if field
%p
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
%p.light
= service_event_description(event)
- @service.fields.each do |field| - @service.global_fields.each do |field|
- type = field[:type] - type = field[:type]
- if type == 'fieldset' - if type == 'fieldset'
......
...@@ -156,7 +156,7 @@ ...@@ -156,7 +156,7 @@
- project_ref = cross_project_reference(@project, issuable) - project_ref = cross_project_reference(@project, issuable)
.block.project-reference .block.project-reference
.sidebar-collapsed-icon .sidebar-collapsed-icon.dont-change-state
= clipboard_button(clipboard_text: project_ref) = clipboard_button(clipboard_text: project_ref)
.cross-project-reference.hide-collapsed .cross-project-reference.hide-collapsed
%span %span
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- page_description @user.bio - page_description @user.bio
- content_for :page_specific_javascripts do - content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/d3.js') = page_specific_javascript_tag('lib/d3.js')
= page_specific_javascript_tag('users/application.js') = page_specific_javascript_tag('users/users_bundle.js')
- header_title @user.name, user_path(@user) - header_title @user.name, user_path(@user)
- @no_container = true - @no_container = true
......
...@@ -28,12 +28,12 @@ class EmailsOnPushWorker ...@@ -28,12 +28,12 @@ class EmailsOnPushWorker
:push :push
end end
merge_base_sha = project.merge_base_commit(before_sha, after_sha).try(:sha)
diff_refs = nil diff_refs = nil
compare = nil compare = nil
reverse_compare = false reverse_compare = false
if action == :push if action == :push
merge_base_sha = project.merge_base_commit(before_sha, after_sha).try(:sha)
compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha) compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha)
diff_refs = Gitlab::Diff::DiffRefs.new( diff_refs = Gitlab::Diff::DiffRefs.new(
......
...@@ -81,10 +81,10 @@ module Gitlab ...@@ -81,10 +81,10 @@ module Gitlab
config.assets.precompile << "print.css" config.assets.precompile << "print.css"
config.assets.precompile << "notify.css" config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css" config.assets.precompile << "mailers/*.css"
config.assets.precompile << "graphs/application.js" config.assets.precompile << "graphs/graphs_bundle.js"
config.assets.precompile << "users/application.js" config.assets.precompile << "users/users_bundle.js"
config.assets.precompile << "network/application.js" config.assets.precompile << "network/network_bundle.js"
config.assets.precompile << "profile/application.js" config.assets.precompile << "profile/profile_bundle.js"
config.assets.precompile << "lib/utils/*.js" config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js" config.assets.precompile << "lib/*.js"
config.assets.precompile << "u2f.js" config.assets.precompile << "u2f.js"
......
...@@ -212,7 +212,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin ...@@ -212,7 +212,7 @@ Settings.gitlab.default_projects_features['builds'] = true if Settin
Settings.gitlab.default_projects_features['container_registry'] = true if Settings.gitlab.default_projects_features['container_registry'].nil? 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.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['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project] Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project]
Settings.gitlab['trusted_proxies'] ||= [] Settings.gitlab['trusted_proxies'] ||= []
......
...@@ -6,5 +6,9 @@ ...@@ -6,5 +6,9 @@
Mime::Type.register_alias "text/plain", :diff Mime::Type.register_alias "text/plain", :diff
Mime::Type.register_alias "text/plain", :patch Mime::Type.register_alias "text/plain", :patch
Mime::Type.register_alias 'text/html', :markdown Mime::Type.register_alias "text/html", :markdown
Mime::Type.register_alias 'text/html', :md Mime::Type.register_alias "text/html", :md
Mime::Type.register "video/mp4", :mp4, [], [:m4v, :mov]
Mime::Type.register "video/webm", :webm
Mime::Type.register "video/ogg", :ogv
# CSP headers have to have single quotes, so failures relating to quotes
# inside Ruby string arrays are irrelevant.
# rubocop:disable Lint/PercentStringArray
require 'gitlab/current_settings'
include Gitlab::CurrentSettings
CSP_REPORT_URI = ''
# Content Security Policy Headers
# For more information on CSP see:
# - https://gitlab.com/gitlab-org/gitlab-ce/issues/18231
# - https://developer.mozilla.org/en-US/docs/Web/Security/CSP/CSP_policy_directives
SecureHeaders::Configuration.default do |config|
# Mark all cookies as "Secure", "HttpOnly", and "SameSite=Strict".
config.cookies = {
secure: true,
httponly: true,
samesite: {
strict: true
}
}
config.x_content_type_options = "nosniff"
config.x_xss_protection = "1; mode=block"
config.x_download_options = "noopen"
config.x_permitted_cross_domain_policies = "none"
config.referrer_policy = "origin-when-cross-origin"
config.csp = {
# "Meta" values.
report_only: true,
preserve_schemes: true,
# "Directive" values.
# Default source allows nothing, more permissive values are set per-policy.
default_src: %w('none'),
# (Deprecated) Don't allow iframes.
frame_src: %w('none'),
# Only allow XMLHTTPRequests from the GitLab instance itself.
connect_src: %w('self'),
# Only load local fonts.
font_src: %w('self'),
# Load local images, any external image available over HTTPS.
img_src: %w(* 'self' data:),
# Audio and video can't be played on GitLab currently, so it's disabled.
media_src: %w('none'),
# Don't allow <object>, <embed>, or <applet> elements.
object_src: %w('none'),
# Allow local scripts and inline scripts.
script_src: %w('unsafe-inline' 'unsafe-eval' 'self'),
# Allow local stylesheets and inline styles.
style_src: %w('unsafe-inline' 'self'),
# The URIs that a user agent may use as the document base URL.
base_uri: %w('self'),
# Only allow local iframes and service workers
child_src: %w('self'),
# Only submit form information to the GitLab instance.
form_action: %w('self'),
# Disallow any parents from embedding a page in an iframe.
frame_ancestors: %w('none'),
# Don't allow any plugins (Flash, Shockwave, etc.)
plugin_types: %w(),
# Blocks all mixed (HTTP) content.
block_all_mixed_content: true,
# Upgrades insecure requests to HTTPS when possible.
upgrade_insecure_requests: true
}
config.csp[:report_uri] = %W(#{CSP_REPORT_URI})
# Allow Bootstrap Linter in development mode.
if Rails.env.development?
config.csp[:script_src] << "maxcdn.bootstrapcdn.com"
end
# reCAPTCHA
if current_application_settings.recaptcha_enabled
config.csp[:script_src] << "https://www.google.com/recaptcha/"
config.csp[:script_src] << "https://www.gstatic.com/recaptcha/"
config.csp[:frame_src] << "https://www.google.com/recaptcha/"
config.x_frame_options = "SAMEORIGIN"
end
# Gravatar
if current_application_settings.gravatar_enabled?
config.csp[:img_src] << "www.gravatar.com"
config.csp[:img_src] << "secure.gravatar.com"
config.csp[:img_src] << Gitlab.config.gravatar.host
end
# Piwik
if Gitlab.config.extra.has_key?('piwik_url') && Gitlab.config.extra.has_key?('piwik_site_id')
config.csp[:script_src] << Gitlab.config.extra.piwik_url
config.csp[:img_src] << Gitlab.config.extra.piwik_url
end
# Google Analytics
if Gitlab.config.extra.has_key?('google_analytics_id')
config.csp[:script_src] << "https://www.google-analytics.com"
end
end
...@@ -18,7 +18,8 @@ Sidekiq.configure_server do |config| ...@@ -18,7 +18,8 @@ Sidekiq.configure_server do |config|
if cron_jobs[k] && cron_jobs_required_keys.all? { |s| cron_jobs[k].key?(s) } if cron_jobs[k] && cron_jobs_required_keys.all? { |s| cron_jobs[k].key?(s) }
cron_jobs[k]['class'] = cron_jobs[k].delete('job_class') cron_jobs[k]['class'] = cron_jobs[k].delete('job_class')
else else
raise("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.") cron_jobs.delete(k)
Rails.logger.error("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.")
end end
end end
Sidekiq::Cron::Job.load_from_hash! cron_jobs Sidekiq::Cron::Job.load_from_hash! cron_jobs
......
...@@ -732,6 +732,10 @@ Rails.application.routes.draw do ...@@ -732,6 +732,10 @@ Rails.application.routes.draw do
resources :triggers, only: [:index, :create, :destroy] resources :triggers, only: [:index, :create, :destroy]
resources :pipelines, only: [:index, :new, :create, :show] do resources :pipelines, only: [:index, :new, :create, :show] do
collection do
resource :pipelines_settings, path: 'settings', only: [:show, :update]
end
member do member do
post :cancel post :cancel
post :retry post :retry
......
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddDomainBlacklistToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
add_column :application_settings, :domain_blacklist_enabled, :boolean, default: false
add_column :application_settings, :domain_blacklist, :text
end
end
class AddRequestAccessEnabledToProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
add_column_with_default :projects, :request_access_enabled, :boolean, default: true
end
def down
remove_column :projects, :request_access_enabled
end
end
class AddRequestAccessEnabledToGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
def up
add_column_with_default :namespaces, :request_access_enabled, :boolean, default: true
end
def down
remove_column :namespaces, :request_access_enabled
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RenameApplicationSettingsRestrictedSignupDomains < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
# method is the _only_ method called in the migration, any other changes
# should go in a separate migration. This ensures that upon failure _only_ the
# index creation fails and can be retried or reverted easily.
#
# To disable transactions uncomment the following line and remove these
# comments:
# disable_ddl_transaction!
def change
rename_column :application_settings, :restricted_signup_domains, :domain_whitelist
end
end
class DropAndReaddHasExternalWikiInProjects < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
remove_column :projects, :has_external_wiki, :boolean
add_column :projects, :has_external_wiki, :boolean
end
def down
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20160718153603) do ActiveRecord::Schema.define(version: 20160721081015) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -49,7 +49,7 @@ ActiveRecord::Schema.define(version: 20160718153603) do ...@@ -49,7 +49,7 @@ ActiveRecord::Schema.define(version: 20160718153603) do
t.integer "max_attachment_size", default: 10, null: false t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility" t.integer "default_project_visibility"
t.integer "default_snippet_visibility" t.integer "default_snippet_visibility"
t.text "restricted_signup_domains" t.text "domain_whitelist"
t.boolean "user_oauth_applications", default: true t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path" t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false t.integer "session_expire_delay", default: 10080, null: false
...@@ -88,6 +88,8 @@ ActiveRecord::Schema.define(version: 20160718153603) do ...@@ -88,6 +88,8 @@ ActiveRecord::Schema.define(version: 20160718153603) do
t.text "after_sign_up_text" t.text "after_sign_up_text"
t.string "repository_storage", default: "default" t.string "repository_storage", default: "default"
t.string "enabled_git_access_protocol" t.string "enabled_git_access_protocol"
t.boolean "domain_blacklist_enabled", default: false
t.text "domain_blacklist"
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
...@@ -675,6 +677,7 @@ ActiveRecord::Schema.define(version: 20160718153603) do ...@@ -675,6 +677,7 @@ ActiveRecord::Schema.define(version: 20160718153603) do
t.string "avatar" t.string "avatar"
t.boolean "share_with_group_lock", default: false t.boolean "share_with_group_lock", default: false
t.integer "visibility_level", default: 20, null: false t.integer "visibility_level", default: 20, null: false
t.boolean "request_access_enabled", default: true, null: false
end end
add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
...@@ -844,6 +847,7 @@ ActiveRecord::Schema.define(version: 20160718153603) do ...@@ -844,6 +847,7 @@ ActiveRecord::Schema.define(version: 20160718153603) do
t.boolean "has_external_issue_tracker" t.boolean "has_external_issue_tracker"
t.string "repository_storage", default: "default", null: false t.string "repository_storage", default: "default", null: false
t.boolean "has_external_wiki" t.boolean "has_external_wiki"
t.boolean "request_access_enabled", default: true, null: false
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
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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