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"
services:
- mysql:latest
- redis:alpine
cache:
key: "ruby21"
paths:
......@@ -34,7 +30,6 @@ stages:
- post-test
# Prepare and merge knapsack tests
.knapsack-state: &knapsack-state
services: []
variables:
......@@ -68,8 +63,14 @@ update-knapsack:
# Execute all testing suites
.use-db: &use-db
services:
- mysql:latest
- redis:alpine
.rspec-knapsack: &rspec-knapsack
stage: test
<<: *use-db
script:
- bundle exec rake assets:precompile 2>/dev/null
- JOB_NAME=( $CI_BUILD_NAME )
......@@ -85,6 +86,7 @@ update-knapsack:
.spinach-knapsack: &spinach-knapsack
stage: test
<<: *use-db
script:
- bundle exec rake assets:precompile 2>/dev/null
- JOB_NAME=( $CI_BUILD_NAME )
......@@ -133,6 +135,7 @@ spinach 9 10: *spinach-knapsack
# Execute all testing suites against Ruby 2.3
.ruby-23: &ruby-23
image: "ruby:2.3"
<<: *use-db
only:
- master
cache:
......@@ -148,7 +151,7 @@ spinach 9 10: *spinach-knapsack
.spinach-knapsack-ruby23: &spinach-knapsack-ruby23
<<: *spinach-knapsack
<<: *ruby-23
rspec 0 20 ruby23: *rspec-knapsack-ruby23
rspec 1 20 ruby23: *rspec-knapsack-ruby23
rspec 2 20 ruby23: *rspec-knapsack-ruby23
......@@ -183,22 +186,41 @@ spinach 9 10 ruby23: *spinach-knapsack-ruby23
# Other generic tests
.static-analyses-variables: &static-analyses-variables
variables:
SIMPLECOV: "false"
USE_DB: "false"
USE_BUNDLE_INSTALL: "true"
.exec: &exec
<<: *static-analyses-variables
stage: test
script:
- bundle exec $CI_BUILD_NAME
teaspoon: *exec
rubocop: *exec
rake scss_lint: *exec
rake brakeman: *exec
rake flog: *exec
rake flay: *exec
rake db:migrate:reset: *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:
stage: test
<<: *static-analyses-variables
only:
- master
script:
......
......@@ -291,6 +291,10 @@ Style/MultilineMethodDefinitionBraceLayout:
Style/MultilineOperationIndentation:
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).
Style/NegatedIf:
Enabled: true
......
......@@ -226,10 +226,6 @@ Style/LineEndConcatenation:
Style/MethodCallParentheses:
Enabled: false
# Offense count: 3
Style/MultilineTernaryOperator:
Enabled: false
# Offense count: 62
# Cop supports --auto-correct.
Style/MutableConstant:
......
This diff is collapsed.
......@@ -349,6 +349,3 @@ gem 'health_check', '~> 2.1.0'
# System information
gem 'vmstat', '~> 2.1.0'
gem 'sys-filesystem', '~> 1.1.6'
# Secure headers for Content Security Policy
gem 'secure_headers', '~> 3.3'
......@@ -645,8 +645,6 @@ GEM
sdoc (0.3.20)
json (>= 1.1.3)
rdoc (~> 3.10)
secure_headers (3.3.2)
useragent
seed-fu (2.3.6)
activerecord (>= 3.1)
activesupport (>= 3.1)
......@@ -769,7 +767,6 @@ GEM
get_process_mem (~> 0)
unicorn (>= 4, < 6)
uniform_notifier (1.9.0)
useragent (0.16.7)
uuid (2.3.8)
macaddr (~> 1.0)
version_sorter (2.0.0)
......@@ -947,7 +944,6 @@ DEPENDENCIES
sass-rails (~> 5.0.0)
scss_lint (~> 0.47.0)
sdoc (~> 0.3.20)
secure_headers (~> 3.3)
seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9)
sentry-raven (~> 1.1.0)
......
......@@ -38,3 +38,14 @@ class @Admin
$('li.group_member').bind 'ajax:success', ->
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
UNFOLDABLE_LINE_CLASS = 'js-unfold'
EMPTY_CELL_CLASS = 'empty-cell'
OLD_LINE_CLASS = 'old_line'
NEW_CLASS = 'new'
LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
TEXT_FILE_SELECTOR = '.text-file'
DEBOUNCE_TIMEOUT_DURATION = 100
......@@ -18,6 +17,8 @@ class @FilesCommentButton
debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION
$(document)
.off 'mouseover', LINE_COLUMN_CLASSES
.off 'mouseleave', LINE_COLUMN_CLASSES
.on 'mouseover', LINE_COLUMN_CLASSES, debounce
.on 'mouseleave', LINE_COLUMN_CLASSES, @destroy
......@@ -64,20 +65,20 @@ class @FilesCommentButton
getLineContent: (hoveredElement) ->
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) ->
if @VIEW_TYPE is 'inline'
return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS
$(".#{OLD_LINE_CLASS}", hoveredElement.parent())
hoveredElement.parent().find ".#{OLD_LINE_CLASS}"
else
return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS
$(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
diffTypeClass: (hoveredElement) ->
if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
$(hoveredElement).prev ".#{LINE_NUMBER_CLASS}"
isMovingToSameType: (e) ->
newButtonParent = @getButtonParent $(e.toElement)
......
......@@ -210,9 +210,22 @@ class GitLabDropdown
data: =>
return @fullData
callback: (data) =>
currentIndex = -1
@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
@dropdown.on "shown.bs.dropdown", @opened
......
......@@ -55,10 +55,13 @@ class @MergeRequestWidget
$('.mr-state-widget').replaceWith(data)
ciLabelForStatus: (status) ->
if status is 'success'
'passed'
else
status
switch status
when 'success'
'passed'
when 'success_with_warnings'
'passed with warnings'
else
status
pollCIStatus: ->
@fetchBuildStatusInterval = setInterval ( =>
......@@ -116,7 +119,7 @@ class @MergeRequestWidget
showCIStatus: (state) ->
return if not state?
$('.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
$('.ci_widget.ci-' + state).show()
switch state
......@@ -124,7 +127,7 @@ class @MergeRequestWidget
@setMergeButtonClass('btn-danger')
when "running"
@setMergeButtonClass('btn-warning')
when "success"
when "success", "success_with_warnings"
@setMergeButtonClass('btn-create')
else
$('.ci_widget.ci-error').show()
......
......@@ -120,6 +120,9 @@ class @Sidebar
i.show()
sidebarCollapseClicked: (e) ->
return if $(e.currentTarget).hasClass('dont-change-state')
sidebar = e.data
e.preventDefault()
$block = $(@).closest('.block')
......
......@@ -232,7 +232,9 @@
.nav-block {
.controls {
float: right;
margin-top: 11px;
margin-top: 8px;
padding-bottom: 7px;
border-bottom: 1px solid $border-color;
}
}
......
......@@ -98,13 +98,30 @@
.md {
&.md-preview-holder {
code {
white-space: pre-wrap;
word-break: keep-all;
}
// 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 {
white-space: pre-wrap;
word-break: keep-all;
}
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);
}
}
.toolbar-group {
......
......@@ -176,3 +176,11 @@
}
}
}
// hide event scope (namespace + project) where it is not necessary
.project-activity {
.event-scope {
display: none;
}
}
......@@ -269,7 +269,7 @@
.issuable-header-btn {
background: $gray-normal;
border: 1px solid $border-gray-normal;
&:hover {
background: $gray-dark;
border: 1px solid $border-gray-dark;
......
......@@ -70,6 +70,14 @@
color: $gl-success;
}
&.ci-success_with_warnings {
color: $gl-success;
i {
color: $gl-warning;
}
}
&.ci-skipped {
background-color: #eee;
color: #888;
......
......@@ -91,34 +91,11 @@ ul.notes {
// 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 {
white-space: pre-wrap;
}
ul.task-list {
ul:not(.task-list) {
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,18 +333,53 @@ a.deploy-project-label {
}
.fork-namespaces {
.fork-thumbnail {
text-align: center;
margin-bottom: $gl-padding;
.caption {
padding: $gl-padding 0;
min-height: 30px;
}
.row {
-webkit-flex-wrap: wrap;
display: -webkit-flex;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
.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;
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 {
min-height: 30px;
padding: $gl-padding 0;
}
}
img {
@include border-radius(50%);
max-width: 100px;
img {
@include border-radius(50%);
max-width: 100px;
}
}
}
}
......
......@@ -15,7 +15,8 @@
border-color: $gl-danger;
}
&.ci-success {
&.ci-success,
&.ci-success_with_warnings {
color: $gl-success;
border-color: $gl-success;
}
......@@ -57,9 +58,12 @@
.ci-status-icon-failed {
color: $gl-danger;
}
.ci-status-icon-pending {
.ci-status-icon-pending,
.ci-status-icon-success_with_warning {
color: $gl-warning;
}
.ci-status-icon-running {
color: $blue-normal;
}
......
......@@ -64,6 +64,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
params[:application_setting][:disabled_oauth_sign_in_sources] =
AuthHelper.button_based_providers.map(&:to_s) -
Array(enabled_oauth_sign_in_sources)
params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file]
params.require(:application_setting).permit(
:default_projects_limit,
......@@ -83,7 +84,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:default_project_visibility,
:default_snippet_visibility,
:default_group_visibility,
:restricted_signup_domains_raw,
:domain_whitelist_raw,
:domain_blacklist_enabled,
:domain_blacklist_raw,
:domain_blacklist_file,
:version_check_enabled,
:admin_notification_email,
:user_oauth_applications,
......
......@@ -60,6 +60,6 @@ class Admin::GroupsController < Admin::ApplicationController
end
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
class Admin::ServicesController < Admin::ApplicationController
include ServiceParams
before_action :service, only: [:edit, :update]
def index
......@@ -13,7 +15,7 @@ class Admin::ServicesController < Admin::ApplicationController
end
def update
if service.update_attributes(application_services_params[:service])
if service.update_attributes(service_params[:service])
redirect_to admin_application_settings_services_path,
notice: 'Application settings saved successfully'
else
......@@ -37,15 +39,4 @@ class Admin::ServicesController < Admin::ApplicationController
def service
@service ||= Service.where(id: params[:id], template: true).first
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
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
end
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
def load_events
......
......@@ -30,7 +30,7 @@ class HelpController < ApplicationController
end
# 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`
path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}")
......
......@@ -3,11 +3,6 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [: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
badge = Gitlab::Badge::Build.new(project, params[:ref])
......
......@@ -15,6 +15,7 @@ class Projects::CompareController < Projects::ApplicationController
end
def show
apply_diff_view_cookie!
end
def diff_for_path
......
......@@ -286,6 +286,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
status = pipeline.status
coverage = pipeline.try(:coverage)
status = "success_with_warnings" if pipeline.success? && pipeline.has_warnings?
status ||= "preparing"
else
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
when "graphs_commits"
commits_namespace_project_graph_path(@project.namespace, @project, @id)
when "badges"
namespace_project_badges_path(@project.namespace, @project, ref: @id)
namespace_project_pipelines_settings_path(@project.namespace, @project, ref: @id)
else
namespace_project_commits_path(@project.namespace, @project, @id)
end
......
class Projects::ServicesController < Projects::ApplicationController
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]
include ServiceParams
# Authorize
before_action :authorize_admin_project!
......@@ -33,7 +18,7 @@ class Projects::ServicesController < Projects::ApplicationController
end
def update
if @service.update_attributes(service_params)
if @service.update_attributes(service_params[:service])
redirect_to(
edit_namespace_project_service_path(@project.namespace, @project,
@service.to_param, notice:
......@@ -64,12 +49,4 @@ class Projects::ServicesController < Projects::ApplicationController
def service
@service ||= @project.services.find { |service| service.to_param == params[:id] }
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
class Projects::UploadsController < Projects::ApplicationController
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]
......@@ -24,7 +24,7 @@ class Projects::UploadsController < Projects::ApplicationController
def show
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
end
......@@ -49,7 +49,7 @@ class Projects::UploadsController < Projects::ApplicationController
@uploader
end
def image?
uploader && uploader.file.exists? && uploader.image?
def image_or_video?
uploader && uploader.file.exists? && uploader.image_or_video?
end
end
......@@ -296,7 +296,7 @@ class ProjectsController < Projects::ApplicationController
:issues_tracker_id, :default_branch,
: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,
:public_builds, :only_allow_merge_if_build_succeeds
:public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled
)
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
end
def ci_label_for_status(status)
if status == 'success'
case status
when 'success'
'passed'
when 'success_with_warnings'
'passed with warnings'
else
status
end
......
......@@ -16,16 +16,6 @@ module CommitsHelper
commit_person_link(commit, options.merge(source: :committer))
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)
if diff.deleted_file
"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
def time_interval_in_words(interval_in_seconds)
interval_in_seconds = interval_in_seconds.to_i
minutes = interval_in_seconds / 60
seconds = interval_in_seconds - minutes * 60
......
......@@ -172,7 +172,7 @@ class Ability
rules << :read_build if project.public_builds?
unless owner || project.team.member?(user) || project_group_member?(project, user)
rules << :request_access
rules << :request_access if project.request_access_enabled
end
end
......@@ -373,7 +373,7 @@ class Ability
end
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
rules.flatten
......
......@@ -4,12 +4,20 @@ class ApplicationSetting < ActiveRecord::Base
add_authentication_token_field :health_check_access_token
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 :import_sources
serialize :disabled_oauth_sign_in_sources, Array
serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw
serialize :domain_whitelist, Array
serialize :domain_blacklist, Array
attr_accessor :domain_whitelist_raw, :domain_blacklist_raw
validates :session_expire_delay,
presence: true,
......@@ -62,6 +70,10 @@ class ApplicationSetting < ActiveRecord::Base
validates :enabled_git_access_protocol,
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|
unless value.nil?
value.each do |level|
......@@ -129,7 +141,7 @@ class ApplicationSetting < ActiveRecord::Base
session_expire_delay: Settings.gitlab['session_expire_delay'],
default_project_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],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
max_artifacts_size: Settings.artifacts['max_size'],
......@@ -150,20 +162,30 @@ class ApplicationSetting < ActiveRecord::Base
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end
def restricted_signup_domains_raw
self.restricted_signup_domains.join("\n") unless self.restricted_signup_domains.nil?
def domain_whitelist_raw
self.domain_whitelist.join("\n") unless self.domain_whitelist.nil?
end
def domain_blacklist_raw
self.domain_blacklist.join("\n") unless self.domain_blacklist.nil?
end
def domain_whitelist_raw=(values)
self.domain_whitelist = []
self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR)
self.domain_whitelist.reject! { |d| d.empty? }
self.domain_whitelist
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 restricted_signup_domains_raw=(values)
self.restricted_signup_domains = []
self.restricted_signup_domains = values.split(
/\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
| # or
\s # any whitespace character
| # or
[\r\n] # any number of newline characters
/x)
self.restricted_signup_domains.reject! { |d| d.empty? }
def domain_blacklist_file=(file)
self.domain_blacklist_raw = file.read
end
def runners_registration_token
......
......@@ -12,7 +12,7 @@ module Ci
scope :unstarted, ->() { where(runner_id: nil) }
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 :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
scope :manual_actions, ->() { where(when: :manual) }
......@@ -97,7 +97,7 @@ module Ci
end
def other_actions
pipeline.manual_actions.where.not(id: self)
pipeline.manual_actions.where.not(name: name)
end
def playable?
......@@ -145,7 +145,15 @@ module Ci
end
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
def merge_request
......@@ -430,28 +438,23 @@ module Ci
self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
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
variables = []
variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag?
variables << { key: :CI_BUILD_NAME, value: name, public: true }
variables << { key: :CI_BUILD_STAGE, value: stage, public: true }
variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request
variables = [
{ key: 'CI', value: 'true', public: true },
{ key: 'GITLAB_CI', value: 'true', public: true },
{ key: 'CI_BUILD_ID', value: id.to_s, public: true },
{ 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
end
......
......@@ -20,6 +20,11 @@ module Ci
after_touch :update_state
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)
sha[0...8]
end
......@@ -146,6 +151,10 @@ module Ci
end
end
def has_warnings?
builds.latest.ignored.any?
end
def config_processor
return nil unless ci_yaml_file
return @config_processor if defined?(@config_processor)
......@@ -198,6 +207,12 @@ module Ci
Note.for_commit_id(sha)
end
def predefined_variables
[
{ key: 'CI_PIPELINE_ID', value: id.to_s, public: true }
]
end
private
def build_builds_for_stages(stages, user, status, trigger_request)
......@@ -206,8 +221,9 @@ module Ci
# build builds only for the first stage that has builds available.
#
stages.any? do |stage|
CreateBuildsService.new(self)
.execute(stage, user, status, trigger_request).present?
CreateBuildsService.new(self).
execute(stage, user, status, trigger_request).
any?(&:active?)
end
end
......@@ -226,7 +242,7 @@ module Ci
def keep_around_commits
return unless project
project.repository.keep_around(self.sha)
project.repository.keep_around(self.before_sha)
end
......
......@@ -114,6 +114,14 @@ module Ci
tag_list.any?
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
def tag_constraints
......
......@@ -7,5 +7,13 @@ module Ci
has_many :builds, class_name: 'Ci::Build'
serialize :variables
def user_variables
return [] unless variables
variables.map do |key, value|
{ key: key, value: value, public: false }
end
end
end
end
......@@ -16,7 +16,11 @@ class CommitStatus < ActiveRecord::Base
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 :ordered, -> { order(:name) }
scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) }
......
......@@ -52,10 +52,50 @@ class Issue < ActiveRecord::Base
attributes
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)
return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank?
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('
issues.confidential IS NULL
OR issues.confidential IS FALSE
......
......@@ -429,6 +429,17 @@ class Project < ActiveRecord::Base
repository.commit(ref)
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)
sha = repository.merge_base(first_commit_id, second_commit_id)
repository.commit(sha) if sha
......@@ -1180,4 +1191,74 @@ class Project < ActiveRecord::Base
def ensure_dir_exist
gitlab_shell.add_namespace(repository_storage_path, namespace.path)
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
......@@ -4,6 +4,9 @@ class SlackService < Service
validates :webhook, presence: true, url: true, if: :activated?
def initialize_properties
# Custom serialized properties initialization
self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) }
if properties.nil?
self.properties = {}
self.notify_only_broken_builds = true
......@@ -29,13 +32,15 @@ class SlackService < Service
end
def fields
[
{ type: 'text', name: 'webhook',
placeholder: 'https://hooks.slack.com/services/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'text', name: 'channel', placeholder: '#channel' },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
]
default_fields =
[
{ type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' },
{ type: 'text', name: 'username', placeholder: 'username' },
{ type: 'text', name: 'channel', placeholder: "#general" },
{ type: 'checkbox', name: 'notify_only_broken_builds' },
]
default_fields + build_event_channels
end
def supported_events
......@@ -74,7 +79,10 @@ class SlackService < Service
end
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
if message
......@@ -83,8 +91,35 @@ class SlackService < Service
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
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
project.name_with_namespace.gsub(/\s/, '')
end
......
......@@ -26,7 +26,7 @@ class Service < ActiveRecord::Base
scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) }
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 :without_defaults, -> { where(default: false) }
......@@ -82,6 +82,18 @@ class Service < ActiveRecord::Base
Gitlab::PushDataBuilder.build_sample(project, user)
end
def event_channel_names
[]
end
def event_field(event)
nil
end
def global_fields
fields
end
def supported_events
%w(push tag_push issue merge_request wiki_page)
end
......
......@@ -111,7 +111,7 @@ class User < ActiveRecord::Base
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
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 :set_notification_email, if: ->(user) { user.email_changed? }
before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
......@@ -412,6 +412,8 @@ class User < ActiveRecord::Base
end
# 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)
Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})")
end
......@@ -760,29 +762,6 @@ class User < ActiveRecord::Base
Project.where(id: events)
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?
!solo_owned_groups.present?
end
......@@ -881,4 +860,40 @@ class User < ActiveRecord::Base
self.can_create_group = false
self.projects_limit = 0
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
......@@ -33,16 +33,15 @@ class FileUploader < CarrierWave::Uploader::Base
end
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("]", "\\]")
markdown = "[#{escaped_filename}](#{self.secure_url})"
markdown.prepend("!") if image?
markdown.prepend("!") if image_or_video?
{
alt: filename,
url: self.secure_url,
is_image: image?,
markdown: markdown
}
end
......
# Extra methods for uploader
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?
img_ext = %w(png jpg jpeg gif bmp tiff)
if file.respond_to?(:extension)
img_ext.include?(file.extension.downcase)
else
# Not all CarrierWave storages respond to :extension
ext = file.path.split('.').last.downcase
img_ext.include?(ext)
end
rescue
false
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)
file.extension
else
# Not all CarrierWave storages respond to :extension
File.extname(file.path).delete('.')
end
extensions.include?(extension.downcase)
end
def file_storage?
......
......@@ -109,7 +109,7 @@
Newly registered users will by default be external
%fieldset
%legend Sign-in Restrictions
%legend Sign-up Restrictions
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
......@@ -122,6 +122,49 @@
= f.label :send_user_confirmation_email do
= f.check_box :send_user_confirmation_email
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
.col-sm-offset-2.col-sm-10
.checkbox
......@@ -147,11 +190,6 @@
.col-sm-10
= 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
.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
= f.label :home_page_url, 'Home page URL', class: 'control-label col-sm-2'
.col-sm-10
......@@ -167,11 +205,6 @@
.col-sm-10
= f.text_area :sign_in_text, class: 'form-control', rows: 4
.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
= f.label :help_page_text, class: 'control-label col-sm-2'
.col-sm-10
......@@ -352,4 +385,4 @@
.form-actions
= f.submit 'Save', class: 'btn btn-save'
= f.submit 'Save', class: 'btn btn-save'
\ No newline at end of file
......@@ -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
.form-group
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
- if @group.new_record?
.form-group
.col-sm-offset-2.col-sm-10
......
......@@ -4,11 +4,7 @@
#{time_ago_with_tooltip(event.created_at)}
= cache [event, current_application_settings, "v2.2"] do
- if event.author
= 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:''
= author_avatar(event, size: 40)
- if event.created_project?
= 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
%span.author_name= link_to_author event
%span.event_label{class: event.action_name}
%span{class: event.action_name}
- if event.target
= event.action_name
%strong
......@@ -10,12 +10,7 @@
- else
= event_action_name(event)
= event_preposition(event)
- if event.project
= link_to_project event.project
- else
= event.project_name
= render "events/event_scope", event: event
- if event.target.respond_to?(:title)
.event-body
......
.event-title
%span.author_name= link_to_author event
%span.event_label{class: event.action_name}
%span{class: event.action_name}
= event_action_name(event)
- if event.project
......
.event-title
%span.author_name= link_to_author event
%span.event_label
= event.action_name
= event_note_title_html(event)
at
= event.action_name
= event_note_title_html(event)
- if event.project
= link_to_project event.project
- else
= event.project_name
= render "events/event_scope", event: event
.event-body
.event-note
......
......@@ -2,14 +2,14 @@
.event-title
%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?
%strong= event.ref_name
- else
%strong
= 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?
.event-body
......
......@@ -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
.form-group
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
.form-group
%hr
= f.label :share_with_group_lock, class: 'control-label' do
......
%ul.nav.nav-sidebar
= 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
= icon('bookmark fw')
%span
Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to explore_groups_path, title: 'Groups' do
= icon('group fw')
%span
Groups
= nav_link(controller: :snippets) do
= link_to explore_snippets_path, title: 'Snippets' do
= icon('clipboard fw')
%span
Snippets
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
= icon('question-circle fw')
%span
Help
......@@ -39,7 +39,7 @@
= link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
%span
Triggers
= nav_link(controller: :badges) do
= link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
= nav_link(controller: :pipelines_settings) do
= link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
%span
Badges
CI/CD Pipelines
- content_for :page_specific_javascripts do
= 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 @@
%i.fa.fa-rss
= render 'shared/event_filter'
.content_list{:"data-href" => activity_project_path(@project)}
.content_list.project-activity{:"data-href" => activity_project_path(@project)}
= spinner
: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 @@
%p.commit-title
- 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"
- else
Cant find HEAD commit for this branch
......
......@@ -9,7 +9,8 @@
= cache(cache_key) do
%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-row-title
%span.item-title
......
......@@ -17,6 +17,6 @@
- if local_assigns.fetch(:allow_rollback, false)
= link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do
- if deployment.last?
Retry
Re-deploy
- else
Rollback
......@@ -32,6 +32,10 @@
%strong
= visibility_level_label(@project.visibility_level)
.light= visibility_level_description(@project.visibility_level, @project)
.form-group
= render 'shared/allow_request_access', form: f
.form-group
= f.label :tag_list, "Tags", class: 'label-light'
= f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
......@@ -86,8 +90,6 @@
%hr
= render 'merge_request_settings', f: f
%hr
= render 'builds_settings', f: f
%hr
%fieldset.features.append-bottom-default
%h5.prepend-top-0
Project avatar
......
- 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
.fork-namespaces
- @namespaces.in_groups_of(6, false) do |group|
.row
- group.each do |namespace|
.col-md-2.col-sm-3
- if fork = namespace.find_fork_of(@project)
.fork-thumbnail
= link_to project_path(fork), title: "Visit project fork", class: 'has-tooltip' do
= image_tag namespace_icon(namespace, 100)
.caption
%strong
= namespace.human_name
%div.text-primary
Already forked
- else
.fork-thumbnail
= link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has-tooltip' do
= image_tag namespace_icon(namespace, 100)
.caption
%strong
= namespace.human_name
%p.light
Fork is a copy of a project repository.
.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 do changes without affecting the original project.
- else
%h3 No available namespaces to fork the project
%p.slead
You must have permission to create a project in a namespace before forking.
Forking a repository allows you to make changes without affecting the original project.
.col-lg-9
.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|
.row
- group.each do |namespace|
- avatar = namespace_icon(namespace, 100)
- if fork = namespace.find_fork_of(@project)
.fork-thumbnail.forked
= link_to project_path(fork) do
- if /no_((\w*)_)*avatar/.match(avatar)
.no-avatar
= icon 'question'
- else
= image_tag avatar
.caption
= namespace.human_name
- else
.fork-thumbnail
= link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), method: "POST" do
- if /no_((\w*)_)*avatar/.match(avatar)
.no-avatar
= icon 'question'
- else
= image_tag avatar
.caption
= namespace.human_name
- else
%label.label-light
%span
No available namespaces to fork the project.
%br
%small
You must have permission to create a project in a namespace before forking.
.save-project-loader.hide
.center
%h2
%i.fa.fa-spinner.fa-spin
Forking repository
%p Please wait a moment, this page will automatically refresh when ready.
.save-project-loader.hide
.center
%h2
%i.fa.fa-spinner.fa-spin
Forking repository
%p Please wait a moment, this page will automatically refresh when ready.
......@@ -3,7 +3,7 @@
- content_for :page_specific_javascripts do
= 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
= link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do
......
- if @pipeline
.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_icon_for_status(status)
%span
......
- page_title "Network", @ref
- content_for :page_specific_javascripts do
= 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 "head"
%div{ class: container_class }
......
......@@ -89,9 +89,9 @@
= link_to "#", class: 'btn js-toggle-button import_git' do
%i.fa.fa-git
%span Repo by URL
%div
%div{ class: 'import_gitlab_project' }
- 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
%span GitLab export
......@@ -130,29 +130,29 @@
$(".modal").hide();
});
$('.import_gitlab_project').bind('click', function() {
var _href = $("a.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').bind('click', function() {
var _href = $("a.btn_import_gitlab_project").attr("href");
$(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val());
});
$('.import_gitlab_project').attr('disabled',true)
$('.import_gitlab_project').attr('title', 'Project path required.');
$('.btn_import_gitlab_project').attr('disabled',true)
$('.import_gitlab_project').attr('title', 'Project path and name required.');
$('.import_gitlab_project').click(function( event ) {
if($('.import_gitlab_project').attr('disabled')) {
if($('.btn_import_gitlab_project').attr('disabled')) {
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(){
if($(this).val().length !=0) {
$('.import_gitlab_project').attr('disabled', false);
$('.btn_import_gitlab_project').attr('disabled', false);
$('.import_gitlab_project').attr('title','');
$(".flash-container").html("")
} else {
$('.import_gitlab_project').attr('disabled',true);
$('.import_gitlab_project').attr('title', 'Project path required.');
$('.btn_import_gitlab_project').attr('disabled',true);
$('.import_gitlab_project').attr('title', 'Project path and name required.');
}
});
......
......@@ -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
= icon('trash-o')
.note-body{class: note_editable ? 'js-task-list-container' : ''}
.note-text
.note-text.md
= preserve do
= note.note_html
= 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 @@
.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|
= render 'shared/service_settings', form: form
= form.submit 'Save changes', class: 'btn btn-save'
&nbsp;
- 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 @@
.col-sm-10
= form.check_box :active
- if @service.supported_events.length > 1
.form-group
= form.label :url, "Trigger", class: 'control-label'
.col-sm-10
- if @service.supported_events.include?("push")
%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
= form.check_box :wiki_page_events, class: 'pull-left'
.prepend-left-20
= form.label :wiki_page_events, class: 'list-label' do
%strong Wiki Page events
%p.light
This url will be triggered when a wiki page is created/updated
.form-group
= form.label :url, "Trigger", class: 'control-label'
.col-sm-10
- @service.supported_events.each do |event|
%div
= form.check_box service_event_field_name(event), class: 'pull-left'
.prepend-left-20
= form.label service_event_field_name(event), class: 'list-label' do
%strong
= event.humanize
- 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]
- if type == 'fieldset'
......
......@@ -156,7 +156,7 @@
- project_ref = cross_project_reference(@project, issuable)
.block.project-reference
.sidebar-collapsed-icon
.sidebar-collapsed-icon.dont-change-state
= clipboard_button(clipboard_text: project_ref)
.cross-project-reference.hide-collapsed
%span
......
......@@ -2,7 +2,7 @@
- page_description @user.bio
- content_for :page_specific_javascripts do
= 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)
- @no_container = true
......
......@@ -28,12 +28,12 @@ class EmailsOnPushWorker
:push
end
merge_base_sha = project.merge_base_commit(before_sha, after_sha).try(:sha)
diff_refs = nil
compare = nil
reverse_compare = false
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)
diff_refs = Gitlab::Diff::DiffRefs.new(
......
......@@ -81,10 +81,10 @@ module Gitlab
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
config.assets.precompile << "graphs/application.js"
config.assets.precompile << "users/application.js"
config.assets.precompile << "network/application.js"
config.assets.precompile << "profile/application.js"
config.assets.precompile << "graphs/graphs_bundle.js"
config.assets.precompile << "users/users_bundle.js"
config.assets.precompile << "network/network_bundle.js"
config.assets.precompile << "profile/profile_bundle.js"
config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js"
config.assets.precompile << "u2f.js"
......
......@@ -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['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['restricted_signup_domains'] ||= []
Settings.gitlab['domain_whitelist'] ||= []
Settings.gitlab['import_sources'] ||= %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project]
Settings.gitlab['trusted_proxies'] ||= []
......
......@@ -6,5 +6,9 @@
Mime::Type.register_alias "text/plain", :diff
Mime::Type.register_alias "text/plain", :patch
Mime::Type.register_alias 'text/html', :markdown
Mime::Type.register_alias 'text/html', :md
Mime::Type.register_alias "text/html", :markdown
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|
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')
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
Sidekiq::Cron::Job.load_from_hash! cron_jobs
......
......@@ -732,6 +732,10 @@ Rails.application.routes.draw do
resources :triggers, only: [:index, :create, :destroy]
resources :pipelines, only: [:index, :new, :create, :show] do
collection do
resource :pipelines_settings, path: 'settings', only: [:show, :update]
end
member do
post :cancel
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 @@
#
# 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
enable_extension "plpgsql"
......@@ -49,7 +49,7 @@ ActiveRecord::Schema.define(version: 20160718153603) do
t.integer "max_attachment_size", default: 10, null: false
t.integer "default_project_visibility"
t.integer "default_snippet_visibility"
t.text "restricted_signup_domains"
t.text "domain_whitelist"
t.boolean "user_oauth_applications", default: true
t.string "after_sign_out_path"
t.integer "session_expire_delay", default: 10080, null: false
......@@ -88,6 +88,8 @@ ActiveRecord::Schema.define(version: 20160718153603) do
t.text "after_sign_up_text"
t.string "repository_storage", default: "default"
t.string "enabled_git_access_protocol"
t.boolean "domain_blacklist_enabled", default: false
t.text "domain_blacklist"
end
create_table "audit_events", force: :cascade do |t|
......@@ -665,16 +667,17 @@ ActiveRecord::Schema.define(version: 20160718153603) do
add_index "milestones", ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
create_table "namespaces", force: :cascade do |t|
t.string "name", null: false
t.string "path", null: false
t.string "name", null: false
t.string "path", null: false
t.integer "owner_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "type"
t.string "description", default: "", null: false
t.string "description", default: "", null: false
t.string "avatar"
t.boolean "share_with_group_lock", default: false
t.integer "visibility_level", default: 20, null: false
t.boolean "share_with_group_lock", default: false
t.integer "visibility_level", default: 20, null: false
t.boolean "request_access_enabled", default: true, null: false
end
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
t.boolean "has_external_issue_tracker"
t.string "repository_storage", default: "default", null: false
t.boolean "has_external_wiki"
t.boolean "request_access_enabled", default: true, null: false
end
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