Commit 272abb33 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'ce-to-ee-2017-09-01' into 'master'

CE upstream: Friday

Closes gitaly#184, gitaly#219, #3298, gitaly#486, gitlab-ce#37194, and gitlab-ce#34864

See merge request !2806
parents 75d710bc 2990c23f
...@@ -752,7 +752,7 @@ GEM ...@@ -752,7 +752,7 @@ GEM
retriable (1.4.1) retriable (1.4.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (2.2.0) rouge (2.2.1)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
......
...@@ -2,19 +2,20 @@ import _ from 'underscore'; ...@@ -2,19 +2,20 @@ import _ from 'underscore';
(() => { (() => {
/* /*
* TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints, * TODO: Make these methods more configurable (e.g. stringifyTime condensed or
* stringifyTime condensed or non-condensed, abbreviateTimelengths) * non-condensed, abbreviateTimelengths)
* */ * */
const utils = window.gl.utils = gl.utils || {}; const utils = window.gl.utils = gl.utils || {};
const prettyTime = utils.prettyTime = { const prettyTime = utils.prettyTime = {
/* /*
* Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # } * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # }
* Seconds can be negative or positive, zero or non-zero. * Seconds can be negative or positive, zero or non-zero. Can be configured for any day
* or week length.
*/ */
parseSeconds(seconds) { parseSeconds(seconds, { daysPerWeek = 5, hoursPerDay = 8 } = {}) {
const DAYS_PER_WEEK = 5; const DAYS_PER_WEEK = daysPerWeek;
const HOURS_PER_DAY = 8; const HOURS_PER_DAY = hoursPerDay;
const MINUTES_PER_HOUR = 60; const MINUTES_PER_HOUR = 60;
const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR; const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR;
const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR; const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR;
......
...@@ -734,6 +734,7 @@ ...@@ -734,6 +734,7 @@
#{$selector}.dropdown-menu, #{$selector}.dropdown-menu,
#{$selector}.dropdown-menu-nav { #{$selector}.dropdown-menu-nav {
li { li {
display: block;
padding: 0 1px; padding: 0 1px;
&:hover { &:hover {
......
...@@ -61,6 +61,10 @@ ...@@ -61,6 +61,10 @@
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
} }
.dropdown-menu.dropdown-menu-align-right {
margin-top: -2px;
}
} }
.form-horizontal { .form-horizontal {
...@@ -356,3 +360,7 @@ ...@@ -356,3 +360,7 @@
} }
} }
} }
.member-form-control {
@include new-style-dropdown;
}
...@@ -290,6 +290,7 @@ ...@@ -290,6 +290,7 @@
.dropdown-toggle { .dropdown-toggle {
.fa { .fa {
margin-left: 0;
color: inherit; color: inherit;
} }
} }
......
...@@ -117,11 +117,14 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -117,11 +117,14 @@ class Admin::UsersController < Admin::ApplicationController
user_params_with_pass = user_params.dup user_params_with_pass = user_params.dup
if params[:user][:password].present? if params[:user][:password].present?
user_params_with_pass.merge!( password_params = {
password: params[:user][:password], password: params[:user][:password],
password_confirmation: params[:user][:password_confirmation], password_confirmation: params[:user][:password_confirmation]
password_expires_at: Time.now }
)
password_params[:password_expires_at] = Time.now unless changing_own_password?
user_params_with_pass.merge!(password_params)
end end
respond_to do |format| respond_to do |format|
...@@ -167,6 +170,10 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -167,6 +170,10 @@ class Admin::UsersController < Admin::ApplicationController
protected protected
def changing_own_password?
user == current_user
end
def user def user
@user ||= User.find_by!(username: params[:id]) @user ||= User.find_by!(username: params[:id])
end end
......
...@@ -210,7 +210,7 @@ class ApplicationController < ActionController::Base ...@@ -210,7 +210,7 @@ class ApplicationController < ActionController::Base
end end
def check_password_expiration def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && current_user.allow_password_authentication? if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now && !current_user.ldap_user?
return redirect_to new_profile_password_path return redirect_to new_profile_password_path
end end
end end
......
module RequiresWhitelistedMonitoringClient module RequiresWhitelistedMonitoringClient
extend ActiveSupport::Concern extend ActiveSupport::Concern
include Gitlab::CurrentSettings
included do included do
before_action :validate_ip_whitelisted_or_valid_token! before_action :validate_ip_whitelisted_or_valid_token!
end end
......
class PasswordsController < Devise::PasswordsController class PasswordsController < Devise::PasswordsController
include Gitlab::CurrentSettings
before_action :resource_from_email, only: [:create] before_action :resource_from_email, only: [:create]
before_action :check_password_authentication_available, only: [:create] before_action :prevent_ldap_reset, only: [:create]
before_action :throttle_reset, only: [:create] before_action :throttle_reset, only: [:create]
def edit def edit
...@@ -40,11 +38,11 @@ class PasswordsController < Devise::PasswordsController ...@@ -40,11 +38,11 @@ class PasswordsController < Devise::PasswordsController
self.resource = resource_class.find_by_email(email) self.resource = resource_class.find_by_email(email)
end end
def check_password_authentication_available def prevent_ldap_reset
return if current_application_settings.password_authentication_enabled? && (resource.nil? || resource.allow_password_authentication?) return unless resource&.ldap_user?
redirect_to after_sending_reset_password_instructions_path_for(resource_name), redirect_to after_sending_reset_password_instructions_path_for(resource_name),
alert: "Password authentication is unavailable." alert: "Cannot reset password for LDAP user."
end end
def throttle_reset def throttle_reset
......
...@@ -77,7 +77,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController ...@@ -77,7 +77,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController
end end
def authorize_change_password! def authorize_change_password!
render_404 unless @user.allow_password_authentication? render_404 if @user.ldap_user?
end end
def user_params def user_params
......
...@@ -94,6 +94,6 @@ class Projects::ApplicationController < ApplicationController ...@@ -94,6 +94,6 @@ class Projects::ApplicationController < ApplicationController
end end
def require_pages_enabled! def require_pages_enabled!
not_found unless Gitlab.config.pages.enabled not_found unless @project.pages_available?
end end
end end
...@@ -205,7 +205,7 @@ module ApplicationHelper ...@@ -205,7 +205,7 @@ module ApplicationHelper
end end
def support_url def support_url
current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/' Gitlab::CurrentSettings.current_application_settings.help_page_support_url.presence || promo_url + '/getting-help/'
end end
def page_filter_path(options = {}) def page_filter_path(options = {})
......
...@@ -2,6 +2,8 @@ module ApplicationSettingsHelper ...@@ -2,6 +2,8 @@ module ApplicationSettingsHelper
prepend EE::ApplicationSettingsHelper prepend EE::ApplicationSettingsHelper
extend self extend self
include Gitlab::CurrentSettings
delegate :gravatar_enabled?, delegate :gravatar_enabled?,
:signup_enabled?, :signup_enabled?,
:password_authentication_enabled?, :password_authentication_enabled?,
......
module AuthHelper module AuthHelper
include Gitlab::CurrentSettings
PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze PROVIDERS_WITH_ICONS = %w(twitter github gitlab bitbucket google_oauth2 facebook azure_oauth2 authentiq).freeze
FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos', 'crowd'].freeze FORM_BASED_PROVIDERS = [/\Aldap/, 'kerberos', 'crowd'].freeze
......
...@@ -78,7 +78,8 @@ module LicenseHelper ...@@ -78,7 +78,8 @@ module LicenseHelper
def show_promotions?(selected_user = current_user) def show_promotions?(selected_user = current_user)
return false unless selected_user return false unless selected_user
if current_application_settings.should_check_namespace_plan? if Gitlab::CurrentSettings.current_application_settings
.should_check_namespace_plan?
true true
else else
license = License.current license = License.current
......
module ProjectsHelper module ProjectsHelper
include Gitlab::CurrentSettings
def link_to_project(project) def link_to_project(project)
link_to [project.namespace.becomes(Namespace), project], title: h(project.name) do link_to [project.namespace.becomes(Namespace), project], title: h(project.name) do
title = content_tag(:span, project.name, class: 'project-name') title = content_tag(:span, project.name, class: 'project-name')
......
class BaseMailer < ActionMailer::Base class BaseMailer < ActionMailer::Base
include Gitlab::CurrentSettings
around_action :render_with_default_locale around_action :render_with_default_locale
helper ApplicationHelper helper ApplicationHelper
helper MarkupHelper helper MarkupHelper
attr_accessor :current_user attr_accessor :current_user
helper_method :current_user, :can? helper_method :current_user, :can?, :current_application_settings
default from: proc { default_sender_address.format } default from: proc { default_sender_address.format }
default reply_to: proc { default_reply_to_address.format } default reply_to: proc { default_reply_to_address.format }
......
...@@ -251,6 +251,28 @@ class Commit ...@@ -251,6 +251,28 @@ class Commit
project.repository.next_branch("cherry-pick-#{short_id}", mild: true) project.repository.next_branch("cherry-pick-#{short_id}", mild: true)
end end
def cherry_pick_description(user)
message_body = "(cherry picked from commit #{sha})"
if merged_merge_request?(user)
commits_in_merge_request = merged_merge_request(user).commits
if commits_in_merge_request.present?
message_body << "\n"
commits_in_merge_request.reverse.each do |commit_in_merge|
message_body << "\n#{commit_in_merge.short_id} #{commit_in_merge.title}"
end
end
end
message_body
end
def cherry_pick_message(user)
%Q{#{message}\n\n#{cherry_pick_description(user)}}
end
def revert_description(user) def revert_description(user)
if merged_merge_request?(user) if merged_merge_request?(user)
"This reverts merge request #{merged_merge_request(user).to_reference}" "This reverts merge request #{merged_merge_request(user).to_reference}"
......
module Elastic module Elastic
module ApplicationSearch module ApplicationSearch
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend Gitlab::CurrentSettings
included do included do
include Elasticsearch::Model include Elasticsearch::Model
include Gitlab::CurrentSettings
index_name [Rails.application.class.parent_name.downcase, Rails.env].join('-') index_name [Rails.application.class.parent_name.downcase, Rails.env].join('-')
......
...@@ -28,7 +28,7 @@ module Spammable ...@@ -28,7 +28,7 @@ module Spammable
def submittable_as_spam? def submittable_as_spam?
if user_agent_detail if user_agent_detail
user_agent_detail.submittable? && current_application_settings.akismet_enabled user_agent_detail.submittable? && Gitlab::CurrentSettings.current_application_settings.akismet_enabled
else else
false false
end end
......
...@@ -303,7 +303,13 @@ class Issue < ActiveRecord::Base ...@@ -303,7 +303,13 @@ class Issue < ActiveRecord::Base
end end
end end
def update_project_counter_caches?
state_changed? || confidential_changed?
end
def update_project_counter_caches def update_project_counter_caches
return unless update_project_counter_caches?
Projects::OpenIssuesCountService.new(project).refresh_cache Projects::OpenIssuesCountService.new(project).refresh_cache
end end
......
class IssueAssignee < ActiveRecord::Base class IssueAssignee < ActiveRecord::Base
extend Gitlab::CurrentSettings
belongs_to :issue belongs_to :issue
belongs_to :assignee, class_name: "User", foreign_key: :user_id belongs_to :assignee, class_name: "User", foreign_key: :user_id
...@@ -9,7 +7,7 @@ class IssueAssignee < ActiveRecord::Base ...@@ -9,7 +7,7 @@ class IssueAssignee < ActiveRecord::Base
# EE-specific # EE-specific
def update_elasticsearch_index def update_elasticsearch_index
if current_application_settings.elasticsearch_indexing? if Gitlab::CurrentSettings.current_application_settings.elasticsearch_indexing?
ElasticIndexerWorker.perform_async( ElasticIndexerWorker.perform_async(
:update, :update,
'Issue', 'Issue',
......
...@@ -630,6 +630,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -630,6 +630,8 @@ class MergeRequest < ActiveRecord::Base
self.merge_requests_closing_issues.delete_all self.merge_requests_closing_issues.delete_all
closes_issues(current_user).each do |issue| closes_issues(current_user).each do |issue|
next if issue.is_a?(ExternalIssue)
self.merge_requests_closing_issues.create!(issue: issue) self.merge_requests_closing_issues.create!(issue: issue)
end end
end end
...@@ -971,7 +973,13 @@ class MergeRequest < ActiveRecord::Base ...@@ -971,7 +973,13 @@ class MergeRequest < ActiveRecord::Base
@base_pipeline ||= project.pipelines.find_by(sha: merge_request_diff&.base_commit_sha) @base_pipeline ||= project.pipelines.find_by(sha: merge_request_diff&.base_commit_sha)
end end
def update_project_counter_caches?
state_changed?
end
def update_project_counter_caches def update_project_counter_caches
return unless update_project_counter_caches?
Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache Projects::OpenMergeRequestsCountService.new(target_project).refresh_cache
end end
......
...@@ -200,6 +200,10 @@ class Namespace < ActiveRecord::Base ...@@ -200,6 +200,10 @@ class Namespace < ActiveRecord::Base
parent.present? parent.present?
end end
def subgroup?
has_parent?
end
def soft_delete_without_removing_associations def soft_delete_without_removing_associations
# We can't use paranoia's `#destroy` since this will hard-delete projects. # We can't use paranoia's `#destroy` since this will hard-delete projects.
# Project uses `pending_delete` instead of the acts_as_paranoia gem. # Project uses `pending_delete` instead of the acts_as_paranoia gem.
......
...@@ -22,6 +22,7 @@ class Project < ActiveRecord::Base ...@@ -22,6 +22,7 @@ class Project < ActiveRecord::Base
prepend EE::Project prepend EE::Project
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
extend Gitlab::CurrentSettings
BoardLimitExceeded = Class.new(StandardError) BoardLimitExceeded = Class.new(StandardError)
...@@ -1231,6 +1232,10 @@ class Project < ActiveRecord::Base ...@@ -1231,6 +1232,10 @@ class Project < ActiveRecord::Base
File.join(pages_path, 'public') File.join(pages_path, 'public')
end end
def pages_available?
Gitlab.config.pages.enabled && !namespace.subgroup?
end
def remove_private_deploy_keys def remove_private_deploy_keys
exclude_keys_linked_to_other_projects = <<-SQL exclude_keys_linked_to_other_projects = <<-SQL
NOT EXISTS ( NOT EXISTS (
......
...@@ -51,7 +51,7 @@ class ProjectFeature < ActiveRecord::Base ...@@ -51,7 +51,7 @@ class ProjectFeature < ActiveRecord::Base
default_value_for :repository_access_level, value: ENABLED, allows_nil: false default_value_for :repository_access_level, value: ENABLED, allows_nil: false
after_commit on: :update do after_commit on: :update do
if current_application_settings.elasticsearch_indexing? if Gitlab::CurrentSettings.current_application_settings.elasticsearch_indexing?
ElasticIndexerWorker.perform_async(:update, 'Project', project_id) ElasticIndexerWorker.perform_async(:update, 'Project', project_id)
end end
end end
......
...@@ -3,6 +3,8 @@ class ProtectedBranch < ActiveRecord::Base ...@@ -3,6 +3,8 @@ class ProtectedBranch < ActiveRecord::Base
include ProtectedRef include ProtectedRef
prepend EE::ProtectedRef prepend EE::ProtectedRef
extend Gitlab::CurrentSettings
protected_ref_access_levels :merge, :push protected_ref_access_levels :merge, :push
# Check if branch name is marked as protected in the system # Check if branch name is marked as protected in the system
......
...@@ -934,7 +934,7 @@ class Repository ...@@ -934,7 +934,7 @@ class Repository
committer = user_to_committer(user) committer = user_to_committer(user)
create_commit(message: commit.message, create_commit(message: commit.cherry_pick_message(user),
author: { author: {
email: commit.author_email, email: commit.author_email,
name: commit.author_name, name: commit.author_name,
......
...@@ -11,6 +11,8 @@ class Snippet < ActiveRecord::Base ...@@ -11,6 +11,8 @@ class Snippet < ActiveRecord::Base
include Spammable include Spammable
include Editable include Editable
extend Gitlab::CurrentSettings
cache_markdown_field :title, pipeline: :single_line cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description cache_markdown_field :description
cache_markdown_field :content cache_markdown_field :content
......
...@@ -2,6 +2,7 @@ require 'carrierwave/orm/activerecord' ...@@ -2,6 +2,7 @@ require 'carrierwave/orm/activerecord'
class User < ActiveRecord::Base class User < ActiveRecord::Base
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
extend Gitlab::CurrentSettings
include Gitlab::ConfigHelper include Gitlab::ConfigHelper
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
...@@ -621,7 +622,7 @@ class User < ActiveRecord::Base ...@@ -621,7 +622,7 @@ class User < ActiveRecord::Base
end end
def require_personal_access_token_creation_for_git_auth? def require_personal_access_token_creation_for_git_auth?
return false if allow_password_authentication? || ldap_user? return false if current_application_settings.password_authentication_enabled? || ldap_user?
PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none? PersonalAccessTokensFinder.new(user: self, impersonation: false, state: 'active').execute.none?
end end
......
...@@ -84,7 +84,7 @@ class WikiPage ...@@ -84,7 +84,7 @@ class WikiPage
# The formatted title of this page. # The formatted title of this page.
def title def title
if @attributes[:title] if @attributes[:title]
self.class.unhyphenize(@attributes[:title]) CGI.unescape_html(self.class.unhyphenize(@attributes[:title]))
else else
"" ""
end end
......
require_dependency 'declarative_policy' require_dependency 'declarative_policy'
class BasePolicy < DeclarativePolicy::Base class BasePolicy < DeclarativePolicy::Base
include Gitlab::CurrentSettings
desc "User is an instance admin" desc "User is an instance admin"
with_options scope: :user, score: 0 with_options scope: :user, score: 0
condition(:admin) { @user&.admin? } condition(:admin) { @user&.admin? }
...@@ -15,7 +13,7 @@ class BasePolicy < DeclarativePolicy::Base ...@@ -15,7 +13,7 @@ class BasePolicy < DeclarativePolicy::Base
desc "The application is restricted from public visibility" desc "The application is restricted from public visibility"
condition(:restricted_public_level, scope: :global) do condition(:restricted_public_level, scope: :global) do
current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) Gitlab::CurrentSettings.current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end end
# EE Extensions # EE Extensions
......
class AkismetService class AkismetService
include Gitlab::CurrentSettings
attr_accessor :owner, :text, :options attr_accessor :owner, :text, :options
def initialize(owner, text, options = {}) def initialize(owner, text, options = {})
......
module Auth module Auth
class ContainerRegistryAuthenticationService < BaseService class ContainerRegistryAuthenticationService < BaseService
include Gitlab::CurrentSettings extend Gitlab::CurrentSettings
AUDIENCE = 'container_registry'.freeze AUDIENCE = 'container_registry'.freeze
......
module Projects module Projects
class AfterImportService class AfterImportService
RESERVED_REFS_REGEXP = RESERVED_REF_PREFIXES = Repository::RESERVED_REFS_NAMES.map { |n| File.join('refs', n, '/') }
%r{\Arefs/(?:#{Regexp.union(*Repository::RESERVED_REFS_NAMES)})/}
def initialize(project) def initialize(project)
@project = project @project = project
...@@ -9,7 +8,7 @@ module Projects ...@@ -9,7 +8,7 @@ module Projects
def execute def execute
Projects::HousekeepingService.new(@project).execute do Projects::HousekeepingService.new(@project).execute do
repository.delete_refs(*garbage_refs) repository.delete_all_refs_except(RESERVED_REF_PREFIXES)
end end
rescue Projects::HousekeepingService::LeaseTaken => e rescue Projects::HousekeepingService::LeaseTaken => e
Rails.logger.info( Rails.logger.info(
...@@ -18,10 +17,6 @@ module Projects ...@@ -18,10 +17,6 @@ module Projects
private private
def garbage_refs
@garbage_refs ||= repository.all_ref_names_except(RESERVED_REFS_REGEXP)
end
def repository def repository
@repository ||= @project.repository @repository ||= @project.repository
end end
......
module Projects module Projects
class UpdatePagesService < BaseService class UpdatePagesService < BaseService
include Gitlab::CurrentSettings
BLOCK_SIZE = 32.kilobytes BLOCK_SIZE = 32.kilobytes
MAX_SIZE = 1.terabyte MAX_SIZE = 1.terabyte
SITE_PATH = 'public/'.freeze SITE_PATH = 'public/'.freeze
......
...@@ -36,7 +36,8 @@ module SlashCommands ...@@ -36,7 +36,8 @@ module SlashCommands
def valid_token? def valid_token?
ActiveSupport::SecurityUtils.variable_size_secure_compare( ActiveSupport::SecurityUtils.variable_size_secure_compare(
current_application_settings.slack_app_verification_token, Gitlab::CurrentSettings.current_application_settings
.slack_app_verification_token,
params[:token] params[:token]
) )
end end
......
class UploadService class UploadService
include Gitlab::CurrentSettings
def initialize(model, file, uploader_class = FileUploader) def initialize(model, file, uploader_class = FileUploader)
@model, @file, @uploader_class = model, file, uploader_class @model, @file, @uploader_class = model, file, uploader_class
end end
......
module Users module Users
class BuildService < BaseService class BuildService < BaseService
prepend ::EE::Users::BuildService prepend ::EE::Users::BuildService
include Gitlab::CurrentSettings
def initialize(current_user, params = {}) def initialize(current_user, params = {})
@current_user = current_user @current_user = current_user
......
...@@ -191,7 +191,7 @@ ...@@ -191,7 +191,7 @@
.checkbox .checkbox
= f.label :password_authentication_enabled do = f.label :password_authentication_enabled do
= f.check_box :password_authentication_enabled = f.check_box :password_authentication_enabled
Password authentication enabled Sign-in enabled
- if omniauth_enabled? && button_based_providers.any? - if omniauth_enabled? && button_based_providers.any?
.form-group .form-group
= f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2' = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2'
......
...@@ -213,7 +213,7 @@ ...@@ -213,7 +213,7 @@
= link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do = link_to project_settings_ci_cd_path(@project), title: 'CI / CD' do
%span %span
CI / CD CI / CD
- if Gitlab.config.pages.enabled - if @project.pages_available?
= nav_link(controller: :pages) do = nav_link(controller: :pages) do
= link_to project_pages_path(@project), title: 'Pages' do = link_to project_pages_path(@project), title: 'Pages' do
%span %span
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
= link_to profile_emails_path, title: 'Emails' do = link_to profile_emails_path, title: 'Emails' do
%span %span
Emails Emails
- if current_user.allow_password_authentication? - unless current_user.ldap_user?
= nav_link(controller: :passwords) do = nav_link(controller: :passwords) do
= link_to edit_profile_password_path, title: 'Password' do = link_to edit_profile_password_path, title: 'Password' do
%span %span
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
= link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do = link_to project_settings_ci_cd_path(@project), title: 'Pipelines' do
%span %span
Pipelines Pipelines
- if Gitlab.config.pages.enabled - if @project.pages_available?
= nav_link(controller: :pages) do = nav_link(controller: :pages) do
= link_to project_pages_path(@project), title: 'Pages' do = link_to project_pages_path(@project), title: 'Pages' do
%span %span
......
---
title: Changes the password change workflow for admins.
merge_request: 13901
author:
type: fixed
---
title: Unescape HTML characters in Wiki title
merge_request: 13942
author: Jacopo Beschi @jacopo-beschi
type: fixed
---
title: Instrument MergeRequest#fetch_ref
merge_request:
author:
type: other
---
title: Fix Merge when pipeline succeeds button dropdown caret icon horizontal alignment
merge_request:
author:
type: fixed
---
title: Reverts changes made to signin_enabled.
merge_request: 13956
author:
type: fixed
---
title: Changed message and title on the 404 page
merge_request:
author: Branka Martinovic
type: added
---
title: Upgrade brace-expansion NPM package due to security issue
merge_request: 13665
author: Markus Koller
type: security
---
title: Make Gitaly PostUploadPack mandatory
merge_request: 13953
author:
type: changed
---
title: Remove closing external issues by reference error
merge_request:
author:
type: fixed
---
title: Bump rouge to v2.2.1
merge_request: 13887
author:
type: other
---
title: Filter additional secrets from Rails logs
merge_request:
author:
type: security
---
title: Only update the sidebar count caches when needed
merge_request:
author:
type: other
---
title: Add 'from commit' information to cherry-picked commits
merge_request: 13475
author: Saverio Miroddi
type: added
---
title: Remove pages settings when not available
merge_request:
author:
type: changed
...@@ -71,31 +71,24 @@ module Gitlab ...@@ -71,31 +71,24 @@ module Gitlab
# Configure sensitive parameters which will be filtered from the log file. # Configure sensitive parameters which will be filtered from the log file.
# #
# Parameters filtered: # Parameters filtered:
# - Password (:password, :password_confirmation) # - Any parameter ending with `_token`
# - Private tokens # - Any parameter containing `password`
# - Any parameter containing `secret`
# - Two-factor tokens (:otp_attempt) # - Two-factor tokens (:otp_attempt)
# - Repo/Project Import URLs (:import_url) # - Repo/Project Import URLs (:import_url)
# - Build variables (:variables) # - Build variables (:variables)
# - GitLab Pages SSL cert/key info (:certificate, :encrypted_key) # - GitLab Pages SSL cert/key info (:certificate, :encrypted_key)
# - Webhook URLs (:hook) # - Webhook URLs (:hook)
# - GitLab-shell secret token (:secret_token)
# - Sentry DSN (:sentry_dsn) # - Sentry DSN (:sentry_dsn)
# - Deploy keys (:key) # - Deploy keys (:key)
config.filter_parameters += [/_token$/, /password/, /secret/]
config.filter_parameters += %i( config.filter_parameters += %i(
authentication_token
certificate certificate
encrypted_key encrypted_key
hook hook
import_url import_url
incoming_email_token
rss_token
key key
otp_attempt otp_attempt
password
password_confirmation
private_token
runners_token
secret_token
sentry_dsn sentry_dsn
variables variables
) )
......
...@@ -148,6 +148,7 @@ def instrument_classes(instrumentation) ...@@ -148,6 +148,7 @@ def instrument_classes(instrumentation)
# Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/36061 # Needed for https://gitlab.com/gitlab-org/gitlab-ce/issues/36061
instrumentation.instrument_instance_method(MergeRequest, :ensure_ref_fetched) instrumentation.instrument_instance_method(MergeRequest, :ensure_ref_fetched)
instrumentation.instrument_instance_method(MergeRequest, :fetch_ref)
end end
# rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/AbcSize
......
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
require 'gitlab/current_settings' require 'gitlab/current_settings'
include Gitlab::CurrentSettings
if Rails.env.production? if Rails.env.production?
# allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done # allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done
begin begin
sentry_enabled = current_application_settings.sentry_enabled sentry_enabled = Gitlab::CurrentSettings.current_application_settings.sentry_enabled
rescue rescue
sentry_enabled = false sentry_enabled = false
end end
if sentry_enabled if sentry_enabled
Raven.configure do |config| Raven.configure do |config|
config.dsn = current_application_settings.sentry_dsn config.dsn = Gitlab::CurrentSettings.current_application_settings.sentry_dsn
config.release = Gitlab::REVISION config.release = Gitlab::REVISION
# Sanitize fields based on those sanitized from Rails. # Sanitize fields based on those sanitized from Rails.
......
# Be sure to restart your server when you modify this file. # Be sure to restart your server when you modify this file.
require 'gitlab/current_settings' require 'gitlab/current_settings'
include Gitlab::CurrentSettings
# allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done # allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done
begin begin
Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay || 10080 Settings.gitlab['session_expire_delay'] = Gitlab::CurrentSettings.current_application_settings.session_expire_delay || 10080
rescue rescue
Settings.gitlab['session_expire_delay'] ||= 10080 Settings.gitlab['session_expire_delay'] ||= 10080
end end
......
...@@ -42,7 +42,7 @@ It is also good practice to check the server's own public key to make sure you ...@@ -42,7 +42,7 @@ It is also good practice to check the server's own public key to make sure you
are not being targeted by a man-in-the-middle attack. To do this, add another are not being targeted by a man-in-the-middle attack. To do this, add another
variable named `SSH_SERVER_HOSTKEYS`. To find out the hostkeys of your server, run variable named `SSH_SERVER_HOSTKEYS`. To find out the hostkeys of your server, run
the `ssh-keyscan YOUR_SERVER` command from a trusted network (ideally, from the the `ssh-keyscan YOUR_SERVER` command from a trusted network (ideally, from the
server itself), and paste its output into the `SSH_SERVER_HOSTKEY` variable. If server itself), and paste its output into the `SSH_SERVER_HOSTKEYS` variable. If
you need to connect to multiple servers, concatenate all the server public keys you need to connect to multiple servers, concatenate all the server public keys
that you collected into the **Value** of the variable. There must be one key per that you collected into the **Value** of the variable. There must be one key per
line. line.
......
# GitLab Helm Chart # GitLab Helm Chart
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these. > **Note:**
* GitLab is working on a [cloud native set of Charts](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md) which will replace these.
> Officially supported cloud providers are Google Container Service and Azure Container Service. * Officially supported cloud providers are Google Container Service and Azure Container Service.
The `gitlab` Helm chart deploys GitLab into your Kubernetes cluster. The `gitlab` Helm chart deploys GitLab into your Kubernetes cluster.
...@@ -22,9 +22,7 @@ This chart includes the following: ...@@ -22,9 +22,7 @@ This chart includes the following:
- [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure - [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure
- The ability to point a DNS entry or URL at your GitLab install - The ability to point a DNS entry or URL at your GitLab install
- The `kubectl` CLI installed locally and authenticated for the cluster - The `kubectl` CLI installed locally and authenticated for the cluster
- The Helm Client installed locally - The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
- The Helm Server (Tiller) already installed and running in the cluster, by running `helm init`
- The GitLab Helm Repo [added to your Helm Client](index.md#add-the-gitlab-helm-repository)
## Configuring GitLab ## Configuring GitLab
...@@ -428,7 +426,7 @@ ingress: ...@@ -428,7 +426,7 @@ ingress:
## Installing GitLab using the Helm Chart ## Installing GitLab using the Helm Chart
> You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically restart. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage. > You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically restart. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage.
Ensure the GitLab repo has been added and re-initialize Helm: Add the GitLab Helm repository and initialize Helm:
```bash ```bash
helm repo add gitlab https://charts.gitlab.io helm repo add gitlab https://charts.gitlab.io
......
# GitLab-Omnibus Helm Chart # GitLab-Omnibus Helm Chart
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these. > **Note:**
* This Helm chart is in beta, while [additional features](https://gitlab.com/charts/charts.gitlab.io/issues/68) are being worked on.
> Officially supported cloud providers are Google Container Service and Azure Container Service. * GitLab is working on a [cloud native set of Charts](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md) which will eventually replace these.
* Officially supported cloud providers are Google Container Service and Azure Container Service.
This work is based partially on: https://github.com/lwolf/kubernetes-gitlab/. GitLab would like to thank Sergey Nuzhdin for his work. This work is based partially on: https://github.com/lwolf/kubernetes-gitlab/. GitLab would like to thank Sergey Nuzhdin for his work.
...@@ -29,53 +30,51 @@ Terms: ...@@ -29,53 +30,51 @@ Terms:
## Prerequisites ## Prerequisites
- _At least_ 4 GB of RAM available on your cluster, in chunks of 1 GB. 41GB of storage and 2 CPU are also required. - _At least_ 4 GB of RAM available on your cluster. 41GB of storage and 2 CPU are also required.
- Kubernetes 1.4+ with Beta APIs enabled - Kubernetes 1.4+ with Beta APIs enabled
- [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure - [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) provisioner support in the underlying infrastructure
- An [external IP address](#networking-prerequisites)
- A [wildcard DNS entry](#networking-prerequisites), which resolves to the external IP address - A [wildcard DNS entry](#networking-prerequisites), which resolves to the external IP address
- The `kubectl` CLI installed locally and authenticated for the cluster - The `kubectl` CLI installed locally and authenticated for the cluster
- The Helm Client installed locally - The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
- The Helm Server (Tiller) already installed and running in the cluster, by running `helm init`
- The GitLab Helm Repo [added to your Helm Client](index.md#add-the-gitlab-helm-repository)
### Networking Prerequisites ### Networking Prerequisites
This chart configures a GitLab server and Kubernetes cluster which can support dynamic [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/index.html), as well as services like the integrated [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html) and [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/). This chart configures a GitLab server and Kubernetes cluster which can support dynamic [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/index.html), as well as services like the integrated [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html) and [Mattermost](https://docs.gitlab.com/omnibus/gitlab-mattermost/).
To support the GitLab services and dynamic environments, a wildcard DNS entry is required which resolves to the external Load Balancer IP. To support the GitLab services and dynamic environments, a wildcard DNS entry is required which resolves to the [Load Balancer](#load-balancer-ip) or [External IP](#external-ip). Configuration of the DNS entry will depend upon the DNS service being used.
#### External IP (Recommended)
To provision an external IP on GCP and Azure, simply request a new address from the Networking section. Ensure that the region matches the region your container cluster is created in. Note, it is important that the IP is not assigned at this point in time. It will be automatically assigned once the Helm chart is installed, and assigned to the Load Balancer. To provision an external IP on GCP and Azure, simply request a new address from the Networking section. Ensure that the region matches the region your container cluster is created in. Note, it is important that the IP is not assigned at this point in time. It will be automatically assigned once the Helm chart is installed, and assigned to the Load Balancer.
Now that an external IP address has been allocated, ensure that the wildcard DNS entry you would like to use resolves to this IP. Please consult the documentation for your DNS service for more information on creating DNS records. Now that an external IP address has been allocated, ensure that the wildcard DNS entry you would like to use resolves to this IP. Please consult the documentation for your DNS service for more information on creating DNS records.
Finally, set the `baseIP` setting to this IP address when [deploying GitLab](#configuring-and-installing-gitlab).
#### Load Balancer IP
If you do not specify a `baseIP`, an ephemeral IP will be assigned to the Load Balancer or Ingress. You can retrieve this IP by running the following command *after* deploying GitLab:
`kubectl get svc -w --namespace nginx-ingress nginx`
The IP address will be displayed in the `EXTERNAL-IP` field, and should be used to configure the Wildcard DNS entry. For more information on creating a wildcard DNS entry, consult the documentation for the DNS server you are using.
For production deployments of GitLab, we strongly recommend using an [External IP](#external-ip).
## Configuring and Installing GitLab ## Configuring and Installing GitLab
For most installations, only two parameters are required: For most installations, only two parameters are required:
- `baseIP`: the desired [external IP address](#networking-prerequisites)
- `baseDomain`: the [base domain](#networking-prerequisites) with the wildcard host entry resolving to the `baseIP`. For example, `mycompany.io`. - `baseDomain`: the [base domain](#networking-prerequisites) with the wildcard host entry resolving to the `baseIP`. For example, `mycompany.io`.
- `legoEmail`: Email address to use when requesting new SSL certificates from Let's Encrypt
Other common configuration options: Other common configuration options:
- `baseIP`: the desired [external IP address](#networking-prerequisites)
- `gitlab`: Choose the [desired edition](https://about.gitlab.com/products), either `ee` or `ce`. `ce` is the default. - `gitlab`: Choose the [desired edition](https://about.gitlab.com/products), either `ee` or `ce`. `ce` is the default.
- `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart - `gitlabEELicense`: For Enterprise Edition, the [license](https://docs.gitlab.com/ee/user/admin_area/license.html) can be installed directly via the Chart
- `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for GCP, with `acs` also supported for Azure. - `provider`: Optimizes the deployment for a cloud provider. The default is `gke` for GCP, with `acs` also supported for Azure.
- `legoEmail`: Email address to use when requesting new SSL certificates from Let's Encrypt
For additional configuration options, consult the [values.yaml](https://gitlab.com/charts/charts.gitlab.io/blob/master/charts/gitlab-omnibus/values.yaml). For additional configuration options, consult the [values.yaml](https://gitlab.com/charts/charts.gitlab.io/blob/master/charts/gitlab-omnibus/values.yaml).
These settings can either be passed directly on the command line:
```bash
helm install --name gitlab --set baseDomain=gitlab.io,baseIP=1.1.1.1,gitlab=ee,gitlabEELicense=$LICENSE,legoEmail=email@gitlab.com gitlab/gitlab-omnibus
```
or within a YAML file:
```bash
helm install --name gitlab -f values.yaml gitlab/gitlab-omnibus
```
> **Note:**
If you are using a machine type with support for less than 4 attached disks, like an Azure trial, you should disable dedicated storage for [Postgres and Redis](#persistent-storage).
### Choosing a different GitLab release version ### Choosing a different GitLab release version
The version of GitLab installed is based on the `gitlab` setting (see [section](#choosing-gitlab-edition) above), and The version of GitLab installed is based on the `gitlab` setting (see [section](#choosing-gitlab-edition) above), and
...@@ -95,6 +94,8 @@ There is no guarantee that other release versions of GitLab, other than what are ...@@ -95,6 +94,8 @@ There is no guarantee that other release versions of GitLab, other than what are
used by default in the chart, will be supported by a chart install. used by default in the chart, will be supported by a chart install.
### Persistent storage ### Persistent storage
> **Note:**
If you are using a machine type with support for less than 4 attached disks, like an Azure trial, you should disable dedicated storage for [Postgres and Redis](#persistent-storage).
By default, persistent storage is enabled for GitLab and the charts it depends By default, persistent storage is enabled for GitLab and the charts it depends
on (Redis and PostgreSQL). on (Redis and PostgreSQL).
...@@ -124,9 +125,10 @@ Ingress routing and SSL are automatically configured within this Chart. An NGINX ...@@ -124,9 +125,10 @@ Ingress routing and SSL are automatically configured within this Chart. An NGINX
Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [xip.io](http://xip.io) and [nip.io](http://nip.io) are unlikely to work. Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [xip.io](http://xip.io) and [nip.io](http://nip.io) are unlikely to work.
## Installing GitLab using the Helm Chart ## Installing GitLab using the Helm Chart
> You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically restart. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage. > **Note:**
You may see a temporary error message `SchedulerPredicates failed due to PersistentVolumeClaim is not bound` while storage provisions. Once the storage provisions, the pods will automatically start. This may take a couple minutes depending on your cloud provider. If the error persists, please review the [prerequisites](#prerequisites) to ensure you have enough RAM, CPU, and storage.
Ensure the GitLab repo has been added and re-initialize Helm: Add the GitLab Helm repository and initialize Helm:
```bash ```bash
helm repo add gitlab https://charts.gitlab.io helm repo add gitlab https://charts.gitlab.io
......
# GitLab Runner Helm Chart # GitLab Runner Helm Chart
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these. > **Note:**
Officially supported cloud providers are Google Container Service and Azure Container Service.
> Officially supported cloud providers are Google Container Service and Azure Container Service.
The `gitlab-runner` Helm chart deploys a GitLab Runner instance into your The `gitlab-runner` Helm chart deploys a GitLab Runner instance into your
Kubernetes cluster. Kubernetes cluster.
...@@ -17,9 +16,7 @@ This chart configures the Runner to: ...@@ -17,9 +16,7 @@ This chart configures the Runner to:
- Your GitLab Server's API is reachable from the cluster - Your GitLab Server's API is reachable from the cluster
- Kubernetes 1.4+ with Beta APIs enabled - Kubernetes 1.4+ with Beta APIs enabled
- The `kubectl` CLI installed locally and authenticated for the cluster - The `kubectl` CLI installed locally and authenticated for the cluster
- The Helm Client installed locally - The [Helm client](https://github.com/kubernetes/helm/blob/master/docs/quickstart.md) installed locally on your machine
- The Helm Server (Tiller) already installed and running in the cluster, by running `helm init`
- The GitLab Helm Repo added to your Helm Client. See [Adding GitLab Helm Repo](index.md#add-the-gitlab-helm-repository)
## Configuring GitLab Runner using the Helm Chart ## Configuring GitLab Runner using the Helm Chart
...@@ -36,6 +33,8 @@ In order for GitLab Runner to function, your config file **must** specify the fo ...@@ -36,6 +33,8 @@ In order for GitLab Runner to function, your config file **must** specify the fo
- `runnerRegistrationToken` - The Registration Token for adding new Runners to the GitLab Server. This must be - `runnerRegistrationToken` - The Registration Token for adding new Runners to the GitLab Server. This must be
retrieved from your GitLab Instance. See the [GitLab Runner Documentation](../../ci/runners/README.md#creating-and-registering-a-runner) for more information. retrieved from your GitLab Instance. See the [GitLab Runner Documentation](../../ci/runners/README.md#creating-and-registering-a-runner) for more information.
Unless you need to specify additional configuration, you are [ready to install](#installing-gitlab-runner-using-the-helm-chart).
### Other configuration ### Other configuration
The rest of the configuration is [documented in the `values.yaml`](https://gitlab.com/charts/charts.gitlab.io/blob/master/charts/gitlab-runner/values.yaml) in the chart repository. The rest of the configuration is [documented in the `values.yaml`](https://gitlab.com/charts/charts.gitlab.io/blob/master/charts/gitlab-runner/values.yaml) in the chart repository.
...@@ -115,6 +114,17 @@ runners: ...@@ -115,6 +114,17 @@ runners:
``` ```
### Controlling maximum Runner concurrency
A single GitLab Runner deployed on Kubernetes is able to execute multiple jobs in parallel by automatically starting additional Runner pods. The [`concurrent` setting](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section) controls the maximum number of pods allowed at a single time, and defaults to `10`.
```yaml
## Configure the maximum number of concurrent jobs
## ref: https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section
##
concurrent: 10
```
### Running Docker-in-Docker containers with GitLab Runners ### Running Docker-in-Docker containers with GitLab Runners
See [Running Privileged Containers for the Runners](#running-privileged-containers-for-the-runners) for how to enable it, See [Running Privileged Containers for the Runners](#running-privileged-containers-for-the-runners) for how to enable it,
...@@ -190,7 +200,7 @@ certsSecretName: <SECRET NAME> ...@@ -190,7 +200,7 @@ certsSecretName: <SECRET NAME>
## Installing GitLab Runner using the Helm Chart ## Installing GitLab Runner using the Helm Chart
Ensure the GitLab repo has been added and re-initialize Helm: Add the GitLab Helm repository and initialize Helm:
```bash ```bash
helm repo add gitlab https://charts.gitlab.io helm repo add gitlab https://charts.gitlab.io
......
# Installing GitLab on Kubernetes # Installing GitLab on Kubernetes
> These Helm charts are in beta. GitLab is working on a [cloud-native](http://docs.gitlab.com/omnibus/package-information/cloud_native.html) set of [Charts](https://gitlab.com/charts/helm.gitlab.io) which will replace these.
> Officially supported cloud providers are Google Container Service and Azure Container Service. > Officially supported cloud providers are Google Container Service and Azure Container Service.
The easiest method to deploy GitLab in [Kubernetes](https://kubernetes.io/) is The easiest method to deploy GitLab in [Kubernetes](https://kubernetes.io/) is
to take advantage of the official GitLab Helm charts. [Helm] is a package to take advantage of GitLab's Helm charts. [Helm] is a package
management tool for Kubernetes, allowing apps to be easily managed via their management tool for Kubernetes, allowing apps to be easily managed via their
Charts. A [Chart] is a detailed description of the application including how it Charts. A [Chart] is a detailed description of the application including how it
should be deployed, upgraded, and configured. should be deployed, upgraded, and configured.
The GitLab Helm repository is located at https://charts.gitlab.io. GitLab provides [official Helm Charts](#official-gitlab-helm-charts-recommended) which is the recommended way to run GitLab with Kubernetes.
You can report any issues related to GitLab's Helm Charts at
There are also two other sets of charts:
* Our [upcoming cloud native Charts](#upcoming-cloud-native-helm-charts), which are in development but will eventually replace the current official charts.
* [Community contributed charts](#community-contributed-helm-charts). These charts should be considered deprecated, in favor of the official charts.
## Official GitLab Helm Charts (Recommended)
These charts utilize our [GitLab Omnibus Docker images](https://docs.gitlab.com/omnibus/docker/README.html). You can report any issues and feedback related to these charts at
https://gitlab.com/charts/charts.gitlab.io/issues. https://gitlab.com/charts/charts.gitlab.io/issues.
Contributions and improvements are also very welcome.
## Prerequisites ### Deploying GitLab on Kubernetes (Recommended)
> *Note*: This chart will eventually be replaced by the [cloud native charts](#upcoming-cloud-native-helm-charts), which are presently in development.
The best way to deploy GitLab on Kubernetes is to use the [gitlab-omnibus](gitlab_omnibus.md) chart. It includes everything needed to run GitLab, including: a [Runner](https://docs.gitlab.com/runner/), [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html#gitlab-container-registry), [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego), and an [Ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). This chart is in beta while [additional features](https://gitlab.com/charts/charts.gitlab.io/issues/68) are being completed.
To use the charts, the Helm tool must be installed and initialized. The best ### Deploying just the GitLab Runner
place to start is by reviewing the [Helm Quick Start Guide][helm-quick].
## Add the GitLab Helm repository To deploy just the GitLab Runner, utilize the [gitlab-runner](gitlab_runner_chart.md) chart. It offers a quick way to configure and deploy the Runner on Kubernetes, regardless of where your GitLab server may be running.
Once Helm has been installed, the GitLab chart repository must be added: ### Advanced deployment of GitLab (Not recommended)
> *Note*: This chart will eventually be replaced by the [cloud native charts](#upcoming-cloud-native-helm-charts), which are presently in development.
```bash If advanced configuration of GitLab is required, the beta [gitlab](gitlab_chart.md) chart can be used which deploys the GitLab service along with optional Postgres and Redis. It offers extensive configuration, but requires deep knowledge of Kubernetes and Helm to use.
helm repo add gitlab https://charts.gitlab.io
```
After adding the repository, Helm must be re-initialized: ## Upcoming Cloud Native Helm Charts
```bash GitLab is working towards a building a [cloud native deployment method](https://gitlab.com/charts/helm.gitlab.io/blob/master/README.md). A key part of this effort is to isolate each service into it's [own Docker container and Helm chart](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420), rather than utilizing the all-in-one container image of the [current charts](#official-gitlab-helm-charts-recommended).
helm init
```
## Using the GitLab Helm Charts By offering individual containers and charts, we will be able to provide a number of benefits:
* Easier horizontal scaling of each service
* Smaller more efficient images
* Potential for rolling updates and canaries within a service
* and plenty more.
GitLab makes available three Helm Charts. This is a large project and will be worked on over the span of multiple releases. For the most up to date status and release information, please see our [tracking issue](https://gitlab.com/gitlab-org/omnibus-gitlab/issues/2420).
- [gitlab-omnibus](gitlab_omnibus.md): **Recommended** and the easiest way to get started. Includes everything needed to run GitLab, including: a [Runner](https://docs.gitlab.com/runner/), [Container Registry](https://docs.gitlab.com/ee/user/project/container_registry.html#gitlab-container-registry), [automatic SSL](https://github.com/kubernetes/charts/tree/master/stable/kube-lego), and an [Ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx). ## Community Contributed Helm Charts
- [gitlab](gitlab_chart.md): Just the GitLab service, with optional Postgres and Redis.
- [gitlab-runner](gitlab_runner_chart.md): GitLab Runner, to process CI jobs.
We are also working on a new set of [cloud native Charts](https://gitlab.com/charts/helm.gitlab.io) which will eventually replace these. The community has also [contributed GitLab charts](https://github.com/kubernetes/charts/tree/master/stable/gitlab-ce) to the [Helm Stable Repository](https://github.com/kubernetes/charts#repository-structure). These charts should be considered [deprecated](https://github.com/kubernetes/charts/issues/1138) in favor of the [official Charts](#official-gitlab-helm-charts-recommended).
[chart]: https://github.com/kubernetes/charts [chart]: https://github.com/kubernetes/charts
[helm-quick]: https://github.com/kubernetes/helm/blob/master/docs/quickstart.md
[helm]: https://github.com/kubernetes/helm/blob/master/README.md [helm]: https://github.com/kubernetes/helm/blob/master/README.md
...@@ -46,7 +46,7 @@ module EE ...@@ -46,7 +46,7 @@ module EE
mirror_data.set_next_execution_timestamp! mirror_data.set_next_execution_timestamp!
end end
if current_application_settings.elasticsearch_indexing? if ::Gitlab::CurrentSettings.current_application_settings.elasticsearch_indexing?
project.run_after_commit do project.run_after_commit do
last_indexed_commit = project.index_status&.last_commit last_indexed_commit = project.index_status&.last_commit
ElasticCommitIndexerWorker.perform_async(project.id, last_indexed_commit) ElasticCommitIndexerWorker.perform_async(project.id, last_indexed_commit)
......
...@@ -7,7 +7,8 @@ module EE ...@@ -7,7 +7,8 @@ module EE
condition(:ldap_synced) { @subject.ldap_synced? } condition(:ldap_synced) { @subject.ldap_synced? }
condition(:can_owners_manage_ldap, scope: :global) do condition(:can_owners_manage_ldap, scope: :global) do
current_application_settings.allow_group_owners_to_manage_ldap ::Gitlab::CurrentSettings.current_application_settings
.allow_group_owners_to_manage_ldap
end end
rule { auditor }.enable :read_group rule { auditor }.enable :read_group
......
...@@ -5,7 +5,6 @@ module EE ...@@ -5,7 +5,6 @@ module EE
# and be prepended in the `PostReceive` worker # and be prepended in the `PostReceive` worker
module PostReceive module PostReceive
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend ::Gitlab::CurrentSettings
private private
...@@ -28,7 +27,8 @@ module EE ...@@ -28,7 +27,8 @@ module EE
end end
def update_wiki_es_indexes(post_received) def update_wiki_es_indexes(post_received)
return unless current_application_settings.elasticsearch_indexing? return unless ::Gitlab::CurrentSettings.current_application_settings
.elasticsearch_indexing?
post_received.project.wiki.index_blobs post_received.project.wiki.index_blobs
end end
......
...@@ -17,7 +17,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps ...@@ -17,7 +17,7 @@ class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
end end
step 'I should see project "Community" home page' do step 'I should see project "Community" home page' do
expect(Gitlab.config.gitlab).to receive(:host).and_return("www.example.com") Gitlab.config.gitlab.should_receive(:host).and_return("www.example.com")
page.within '.breadcrumbs .title' do page.within '.breadcrumbs .title' do
expect(page).to have_content 'Community' expect(page).to have_content 'Community'
end end
......
...@@ -10,7 +10,7 @@ module API ...@@ -10,7 +10,7 @@ module API
params do params do
requires :id, type: String, desc: "The #{source_type} ID" requires :id, type: String, desc: "The #{source_type} ID"
end end
resource source_type.pluralize, requirements: { id: %r{[^/]+} } do resource source_type.pluralize, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "Gets a list of access requests for a #{source_type}." do desc "Gets a list of access requests for a #{source_type}." do
detail 'This feature was introduced in GitLab 8.11.' detail 'This feature was introduced in GitLab 8.11.'
success Entities::AccessRequester success Entities::AccessRequester
......
...@@ -12,7 +12,7 @@ module API ...@@ -12,7 +12,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
AWARDABLES.each do |awardable_params| AWARDABLES.each do |awardable_params|
awardable_string = awardable_params[:type].pluralize awardable_string = awardable_params[:type].pluralize
awardable_id_string = "#{awardable_params[:type]}_#{awardable_params[:find_by]}" awardable_id_string = "#{awardable_params[:type]}_#{awardable_params[:find_by]}"
......
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all project boards' do desc 'Get all project boards' do
detail 'This feature was introduced in 8.13' detail 'This feature was introduced in 8.13'
success Entities::Board success Entities::Board
......
...@@ -5,7 +5,7 @@ module API ...@@ -5,7 +5,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
include PaginationParams include PaginationParams
before { authenticate! } before { authenticate! }
......
...@@ -17,7 +17,7 @@ module API ...@@ -17,7 +17,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of the project' requires :id, type: String, desc: 'The ID of the project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
before { authorize_admin_project } before { authorize_admin_project }
desc "Get a specific project's deploy keys" do desc "Get a specific project's deploy keys" do
......
...@@ -8,7 +8,7 @@ module API ...@@ -8,7 +8,7 @@ module API
params do params do
requires :id, type: String, desc: 'The project ID' requires :id, type: String, desc: 'The project ID'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all deployments of the project' do desc 'Get all deployments of the project' do
detail 'This feature was introduced in GitLab 8.11.' detail 'This feature was introduced in GitLab 8.11.'
success Entities::Deployment success Entities::Deployment
......
...@@ -9,7 +9,7 @@ module API ...@@ -9,7 +9,7 @@ module API
params do params do
requires :id, type: String, desc: 'The project ID' requires :id, type: String, desc: 'The project ID'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all environments of the project' do desc 'Get all environments of the project' do
detail 'This feature was introduced in GitLab 8.11.' detail 'This feature was introduced in GitLab 8.11.'
success Entities::Environment success Entities::Environment
......
...@@ -67,7 +67,7 @@ module API ...@@ -67,7 +67,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "List a Project's visible events" do desc "List a Project's visible events" do
success Entities::Event success Entities::Event
end end
......
...@@ -10,7 +10,7 @@ module API ...@@ -10,7 +10,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a group' requires :id, type: String, desc: 'The ID of a group'
end end
resource :groups, requirements: { id: %r{[^/]+} } do resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of group milestones' do desc 'Get a list of group milestones' do
success Entities::Milestone success Entities::Milestone
end end
......
...@@ -9,7 +9,7 @@ module API ...@@ -9,7 +9,7 @@ module API
requires :id, type: String, desc: 'The ID of a group' requires :id, type: String, desc: 'The ID of a group'
end end
resource :groups, requirements: { id: %r{[^/]+} } do resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get group-level variables' do desc 'Get group-level variables' do
success Entities::Variable success Entities::Variable
end end
......
...@@ -114,7 +114,7 @@ module API ...@@ -114,7 +114,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a group' requires :id, type: String, desc: 'The ID of a group'
end end
resource :groups, requirements: { id: %r{[^/]+} } do resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Update a group. Available only for users who can administrate groups.' do desc 'Update a group. Available only for users who can administrate groups.' do
success Entities::Group success Entities::Group
end end
......
...@@ -42,6 +42,10 @@ module API ...@@ -42,6 +42,10 @@ module API
::Users::ActivityService.new(actor, 'Git SSH').execute if commands.include?(params[:action]) ::Users::ActivityService.new(actor, 'Git SSH').execute if commands.include?(params[:action])
end end
def merge_request_urls
::MergeRequests::GetUrlsService.new(project).execute(params[:changes])
end
private private
def set_project def set_project
......
...@@ -2,6 +2,7 @@ module API ...@@ -2,6 +2,7 @@ module API
module Helpers module Helpers
module Runner module Runner
prepend EE::API::Helpers::Runner prepend EE::API::Helpers::Runner
include Gitlab::CurrentSettings
JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'.freeze JOB_TOKEN_HEADER = 'HTTP_JOB_TOKEN'.freeze
JOB_TOKEN_PARAM = :token JOB_TOKEN_PARAM = :token
......
...@@ -69,7 +69,7 @@ module API ...@@ -69,7 +69,7 @@ module API
end end
get "/merge_request_urls" do get "/merge_request_urls" do
::MergeRequests::GetUrlsService.new(project).execute(params[:changes]) merge_request_urls
end end
# #
...@@ -168,6 +168,21 @@ module API ...@@ -168,6 +168,21 @@ module API
# render_api_error!(e, 500) # render_api_error!(e, 500)
# end # end
end end
post '/post_receive' do
status 200
PostReceive.perform_async(params[:gl_repository], params[:identifier],
params[:changes])
broadcast_message = BroadcastMessage.current&.last&.message
reference_counter_decreased = Gitlab::ReferenceCounter.new(params[:gl_repository]).decrease
{
merge_request_urls: merge_request_urls,
broadcast_message: broadcast_message,
reference_counter_decreased: reference_counter_decreased
}
end
end end
end end
end end
...@@ -86,7 +86,7 @@ module API ...@@ -86,7 +86,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a group' requires :id, type: String, desc: 'The ID of a group'
end end
resource :groups, requirements: { id: %r{[^/]+} } do resource :groups, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of group issues' do desc 'Get a list of group issues' do
success Entities::IssueBasic success Entities::IssueBasic
end end
...@@ -113,7 +113,7 @@ module API ...@@ -113,7 +113,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
include TimeTrackingEndpoints include TimeTrackingEndpoints
desc 'Get a list of project issues' do desc 'Get a list of project issues' do
......
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do helpers do
params :optional_scope do params :optional_scope do
optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show', optional :scope, types: [String, Array[String]], desc: 'The scope of builds to show',
......
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all labels of the project' do desc 'Get all labels of the project' do
success Entities::Label success Entities::Label
end end
......
...@@ -10,7 +10,7 @@ module API ...@@ -10,7 +10,7 @@ module API
params do params do
requires :id, type: String, desc: "The #{source_type} ID" requires :id, type: String, desc: "The #{source_type} ID"
end end
resource source_type.pluralize, requirements: { id: %r{[^/]+} } do resource source_type.pluralize, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Gets a list of group or project members viewable by the authenticated user.' do desc 'Gets a list of group or project members viewable by the authenticated user.' do
success Entities::Member success Entities::Member
end end
......
...@@ -8,7 +8,7 @@ module API ...@@ -8,7 +8,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of merge request diff versions' do desc 'Get a list of merge request diff versions' do
detail 'This feature was introduced in GitLab 8.12.' detail 'This feature was introduced in GitLab 8.12.'
success Entities::MergeRequestDiff success Entities::MergeRequestDiff
......
...@@ -72,7 +72,7 @@ module API ...@@ -72,7 +72,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
include TimeTrackingEndpoints include TimeTrackingEndpoints
helpers do helpers do
......
...@@ -9,7 +9,7 @@ module API ...@@ -9,7 +9,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
NOTEABLE_TYPES.each do |noteable_type| NOTEABLE_TYPES.each do |noteable_type|
noteables_str = noteable_type.to_s.underscore.pluralize noteables_str = noteable_type.to_s.underscore.pluralize
......
...@@ -54,7 +54,7 @@ module API ...@@ -54,7 +54,7 @@ module API
params do params do
requires :id, type: String, desc: "The #{source_type} ID" requires :id, type: String, desc: "The #{source_type} ID"
end end
resource source_type.pluralize, requirements: { id: %r{[^/]+} } do resource source_type.pluralize, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "Get #{source_type} level notification level settings, defaults to Global" do desc "Get #{source_type} level notification level settings, defaults to Global" do
detail 'This feature was introduced in GitLab 8.12' detail 'This feature was introduced in GitLab 8.12'
success Entities::NotificationSetting success Entities::NotificationSetting
......
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all pipeline schedules' do desc 'Get all pipeline schedules' do
success Entities::PipelineSchedule success Entities::PipelineSchedule
end end
......
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
params do params do
requires :id, type: String, desc: 'The project ID' requires :id, type: String, desc: 'The project ID'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get all Pipelines of the project' do desc 'Get all Pipelines of the project' do
detail 'This feature was introduced in GitLab 8.11.' detail 'This feature was introduced in GitLab 8.11.'
success Entities::PipelineBasic success Entities::PipelineBasic
......
...@@ -24,7 +24,7 @@ module API ...@@ -24,7 +24,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get project hooks' do desc 'Get project hooks' do
success Entities::ProjectHook success Entities::ProjectHook
end end
......
...@@ -10,7 +10,7 @@ module API ...@@ -10,7 +10,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a list of project milestones' do desc 'Get a list of project milestones' do
success Entities::Milestone success Entities::Milestone
end end
......
...@@ -7,7 +7,7 @@ module API ...@@ -7,7 +7,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do helpers do
def handle_project_member_errors(errors) def handle_project_member_errors(errors)
if errors[:project_access].any? if errors[:project_access].any?
......
...@@ -101,7 +101,7 @@ module API ...@@ -101,7 +101,7 @@ module API
end end
end end
resource :users, requirements: { user_id: %r{[^/]+} } do resource :users, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a user projects' do desc 'Get a user projects' do
success Entities::BasicProjectDetails success Entities::BasicProjectDetails
end end
...@@ -189,7 +189,7 @@ module API ...@@ -189,7 +189,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get a single project' do desc 'Get a single project' do
success Entities::ProjectWithAccess success Entities::ProjectWithAccess
end end
......
...@@ -9,7 +9,7 @@ module API ...@@ -9,7 +9,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
helpers do helpers do
def handle_project_member_errors(errors) def handle_project_member_errors(errors)
if errors[:project_access].any? if errors[:project_access].any?
......
...@@ -89,7 +89,7 @@ module API ...@@ -89,7 +89,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
before { authorize_admin_project } before { authorize_admin_project }
desc 'Get runners available for project' do desc 'Get runners available for project' do
......
...@@ -649,7 +649,7 @@ module API ...@@ -649,7 +649,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
before { authenticate! } before { authenticate! }
before { authorize_admin_project } before { authorize_admin_project }
...@@ -739,7 +739,7 @@ module API ...@@ -739,7 +739,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc "Trigger a slash command for #{service_slug}" do desc "Trigger a slash command for #{service_slug}" do
detail 'Added in GitLab 8.13' detail 'Added in GitLab 8.13'
end end
......
...@@ -12,7 +12,7 @@ module API ...@@ -12,7 +12,7 @@ module API
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
requires :subscribable_id, type: String, desc: 'The ID of a resource' requires :subscribable_id, type: String, desc: 'The ID of a resource'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
subscribable_types.each do |type, finder| subscribable_types.each do |type, finder|
type_singularized = type.singularize type_singularized = type.singularize
entity_class = Entities.const_get(type_singularized.camelcase) entity_class = Entities.const_get(type_singularized.camelcase)
......
...@@ -12,7 +12,7 @@ module API ...@@ -12,7 +12,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
ISSUABLE_TYPES.each do |type, finder| ISSUABLE_TYPES.each do |type, finder|
type_id_str = "#{type.singularize}_iid".to_sym type_id_str = "#{type.singularize}_iid".to_sym
......
...@@ -5,7 +5,7 @@ module API ...@@ -5,7 +5,7 @@ module API
params do params do
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Trigger a GitLab project pipeline' do desc 'Trigger a GitLab project pipeline' do
success Entities::Pipeline success Entities::Pipeline
end end
......
...@@ -9,7 +9,7 @@ module API ...@@ -9,7 +9,7 @@ module API
requires :id, type: String, desc: 'The ID of a project' requires :id, type: String, desc: 'The ID of a project'
end end
resource :projects, requirements: { id: %r{[^/]+} } do resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
desc 'Get project variables' do desc 'Get project variables' do
success Entities::Variable success Entities::Variable
end end
......
# Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails # Read about interceptors in http://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails
class EmailTemplateInterceptor class EmailTemplateInterceptor
include Gitlab::CurrentSettings extend Gitlab::CurrentSettings
def self.delivering_email(message) def self.delivering_email(message)
# Remove HTML part if HTML emails are disabled. # Remove HTML part if HTML emails are disabled.
......
...@@ -6,6 +6,8 @@ module Gitlab ...@@ -6,6 +6,8 @@ module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters # Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
# the resulting HTML through HTML pipeline filters. # the resulting HTML through HTML pipeline filters.
module Asciidoc module Asciidoc
extend Gitlab::CurrentSettings
DEFAULT_ADOC_ATTRS = [ DEFAULT_ADOC_ATTRS = [
'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab', 'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab',
'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font' 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font'
......
...@@ -20,6 +20,7 @@ module Gitlab ...@@ -20,6 +20,7 @@ module Gitlab
class << self class << self
prepend EE::Gitlab::Auth prepend EE::Gitlab::Auth
include Gitlab::CurrentSettings
def find_for_git_client(login, password, project:, ip:) def find_for_git_client(login, password, project:, ip:)
raise "Must provide an IP for rate limiting" if ip.nil? raise "Must provide an IP for rate limiting" if ip.nil?
...@@ -50,10 +51,6 @@ module Gitlab ...@@ -50,10 +51,6 @@ module Gitlab
# Avoid resource intensive login checks if password is not provided # Avoid resource intensive login checks if password is not provided
return unless password.present? return unless password.present?
# Nothing to do here if internal auth is disabled and LDAP is
# not configured
return unless current_application_settings.password_authentication_enabled? || Gitlab::LDAP::Config.enabled?
Gitlab::Auth::UniqueIpsLimiter.limit_user! do Gitlab::Auth::UniqueIpsLimiter.limit_user! do
user = User.by_login(login) user = User.by_login(login)
......
module Gitlab module Gitlab
module CurrentSettings module CurrentSettings
extend self
def current_application_settings def current_application_settings
if RequestStore.active? if RequestStore.active?
RequestStore.fetch(:current_application_settings) { ensure_application_settings! } RequestStore.fetch(:current_application_settings) { ensure_application_settings! }
......
...@@ -250,11 +250,17 @@ module Gitlab ...@@ -250,11 +250,17 @@ module Gitlab
branch_names + tag_names branch_names + tag_names
end end
def delete_all_refs_except(prefixes)
delete_refs(*all_ref_names_except(prefixes))
end
# Returns an Array of all ref names, except when it's matching pattern # Returns an Array of all ref names, except when it's matching pattern
# #
# regexp - The pattern for ref names we don't want # regexp - The pattern for ref names we don't want
def all_ref_names_except(regexp) def all_ref_names_except(prefixes)
rugged.references.reject { |ref| ref.name =~ regexp }.map(&:name) rugged.references.reject do |ref|
prefixes.any? { |p| ref.name.start_with?(p) }
end.map(&:name)
end end
# Discovers the default branch based on the repository's available branches # Discovers the default branch based on the repository's available branches
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module Gitlab module Gitlab
module GonHelper module GonHelper
include WebpackHelper include WebpackHelper
include Gitlab::CurrentSettings
def add_gon_variables def add_gon_variables
gon.api_version = 'v4' gon.api_version = 'v4'
......
module Gitlab module Gitlab
module Metrics module Metrics
module InfluxDb module InfluxDb
extend Gitlab::CurrentSettings include Gitlab::CurrentSettings
extend self extend self
MUTEX = Mutex.new MUTEX = Mutex.new
......
module Gitlab module Gitlab
module Mirror module Mirror
include Gitlab::CurrentSettings extend Gitlab::CurrentSettings
# Runs scheduler every minute # Runs scheduler every minute
SCHEDULER_CRON = '* * * * *'.freeze SCHEDULER_CRON = '* * * * *'.freeze
......
module Gitlab module Gitlab
module PerformanceBar module PerformanceBar
include Gitlab::CurrentSettings extend Gitlab::CurrentSettings
ALLOWED_USER_IDS_KEY = 'performance_bar_allowed_user_ids:v2'.freeze ALLOWED_USER_IDS_KEY = 'performance_bar_allowed_user_ids:v2'.freeze
EXPIRY_TIME = 5.minutes EXPIRY_TIME = 5.minutes
......
module Gitlab module Gitlab
class PollingInterval class PollingInterval
include Gitlab::CurrentSettings extend Gitlab::CurrentSettings
HEADER_NAME = 'Poll-Interval'.freeze HEADER_NAME = 'Poll-Interval'.freeze
......
module Gitlab module Gitlab
module ProtocolAccess module ProtocolAccess
extend Gitlab::CurrentSettings
def self.allowed?(protocol) def self.allowed?(protocol)
if protocol == 'web' if protocol == 'web'
true true
......
module Gitlab module Gitlab
module Recaptcha module Recaptcha
extend Gitlab::CurrentSettings
def self.load_configurations! def self.load_configurations!
if current_application_settings.recaptcha_enabled if current_application_settings.recaptcha_enabled
::Recaptcha.configure do |config| ::Recaptcha.configure do |config|
......
module Gitlab
class ReferenceCounter
REFERENCE_EXPIRE_TIME = 600
attr_reader :gl_repository, :key
def initialize(gl_repository)
@gl_repository = gl_repository
@key = "git-receive-pack-reference-counter:#{gl_repository}"
end
def value
Gitlab::Redis::SharedState.with { |redis| (redis.get(key) || 0).to_i }
end
def increase
redis_cmd do |redis|
redis.incr(key)
redis.expire(key, REFERENCE_EXPIRE_TIME)
end
end
def decrease
redis_cmd do |redis|
current_value = redis.decr(key)
if current_value < 0
Rails.logger.warn("Reference counter for #{gl_repository} decreased" \
" when its value was less than 1. Reseting the counter.")
redis.del(key)
end
end
end
private
def redis_cmd
Gitlab::Redis::SharedState.with { |redis| yield(redis) }
true
rescue => e
Rails.logger.warn("GitLab: An unexpected error occurred in writing to Redis: #{e}")
false
end
end
end
module Gitlab module Gitlab
module Sentry module Sentry
extend Gitlab::CurrentSettings
def self.enabled? def self.enabled?
Rails.env.production? && current_application_settings.sentry_enabled? Rails.env.production? && current_application_settings.sentry_enabled?
end end
......
...@@ -442,9 +442,9 @@ module Gitlab ...@@ -442,9 +442,9 @@ module Gitlab
def authorized_keys_enabled? def authorized_keys_enabled?
# Return true if nil to ensure the authorized_keys methods work while # Return true if nil to ensure the authorized_keys methods work while
# fixing the authorized_keys file during migration. # fixing the authorized_keys file during migration.
return true if current_application_settings.authorized_keys_enabled.nil? return true if Gitlab::CurrentSettings.current_application_settings.authorized_keys_enabled.nil?
current_application_settings.authorized_keys_enabled Gitlab::CurrentSettings.current_application_settings.authorized_keys_enabled
end end
private private
......
module Gitlab module Gitlab
class UsageData class UsageData
class << self
include Gitlab::CurrentSettings include Gitlab::CurrentSettings
class << self
def data(force_refresh: false) def data(force_refresh: false)
Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) { uncached_data } Rails.cache.fetch('usage_data', force: force_refresh, expires_in: 2.weeks) { uncached_data }
end end
......
...@@ -35,10 +35,7 @@ module Gitlab ...@@ -35,10 +35,7 @@ module Gitlab
when 'git_receive_pack' when 'git_receive_pack'
Gitlab::GitalyClient.feature_enabled?(:post_receive_pack) Gitlab::GitalyClient.feature_enabled?(:post_receive_pack)
when 'git_upload_pack' when 'git_upload_pack'
Gitlab::GitalyClient.feature_enabled?( true
:post_upload_pack,
status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT
)
when 'info_refs' when 'info_refs'
true true
else else
......
...@@ -65,7 +65,7 @@ namespace :gitlab do ...@@ -65,7 +65,7 @@ namespace :gitlab do
puts "URL:\t\t#{Gitlab.config.gitlab.url}" puts "URL:\t\t#{Gitlab.config.gitlab.url}"
puts "HTTP Clone URL:\t#{http_clone_url}" puts "HTTP Clone URL:\t#{http_clone_url}"
puts "SSH Clone URL:\t#{ssh_clone_url}" puts "SSH Clone URL:\t#{ssh_clone_url}"
puts "Elasticsearch:\t#{current_application_settings.elasticsearch_indexing? ? "yes".color(:green) : "no"}" puts "Elasticsearch:\t#{Gitlab::CurrentSettings.current_application_settings.elasticsearch_indexing? ? "yes".color(:green) : "no"}"
puts "Geo:\t\t#{Gitlab::Geo.enabled? ? "yes".color(:green) : "no"}" puts "Geo:\t\t#{Gitlab::Geo.enabled? ? "yes".color(:green) : "no"}"
puts "Geo node:\t#{geo_node_type}" if Gitlab::Geo.enabled? puts "Geo node:\t#{geo_node_type}" if Gitlab::Geo.enabled?
puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".color(:green) : "no"}" puts "Using LDAP:\t#{Gitlab.config.ldap.enabled ? "yes".color(:green) : "no"}"
......
...@@ -80,7 +80,7 @@ class GithubImport ...@@ -80,7 +80,7 @@ class GithubImport
end end
def visibility_level def visibility_level
@repo['private'] ? Gitlab::VisibilityLevel::PRIVATE : current_application_settings.default_project_visibility @repo['private'] ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::CurrentSettings.current_application_settings.default_project_visibility
end end
end end
......
...@@ -72,8 +72,9 @@ ...@@ -72,8 +72,9 @@
404 404
</h1> </h1>
<div class="container"> <div class="container">
<h3>The page you're looking for could not be found.</h3> <h3>The page could not be found or you don't have permission to view it.</h3>
<hr /> <hr />
<p>The resource that you are attempting to access does not exist or you don't have the necessary permissions to view it.</p>
<p>Make sure the address is correct and that the page hasn't moved.</p> <p>Make sure the address is correct and that the page hasn't moved.</p>
<p>Please contact your GitLab administrator if you think this is a mistake.</p> <p>Please contact your GitLab administrator if you think this is a mistake.</p>
<a href="javascript:history.back()" class="js-go-back go-back">Go back</a> <a href="javascript:history.back()" class="js-go-back go-back">Go back</a>
......
...@@ -150,6 +150,18 @@ describe Admin::UsersController do ...@@ -150,6 +150,18 @@ describe Admin::UsersController do
post :update, params post :update, params
end end
context 'when the admin changes his own password' do
it 'updates the password' do
expect { update_password(admin, 'AValidPassword1') }
.to change { admin.reload.encrypted_password }
end
it 'does not set the new password to expire immediately' do
expect { update_password(admin, 'AValidPassword1') }
.not_to change { admin.reload.password_expires_at }
end
end
context 'when the new password is valid' do context 'when the new password is valid' do
it 'redirects to the user' do it 'redirects to the user' do
update_password(user, 'AValidPassword1') update_password(user, 'AValidPassword1')
...@@ -158,15 +170,13 @@ describe Admin::UsersController do ...@@ -158,15 +170,13 @@ describe Admin::UsersController do
end end
it 'updates the password' do it 'updates the password' do
update_password(user, 'AValidPassword1') expect { update_password(user, 'AValidPassword1') }
.to change { user.reload.encrypted_password }
expect { user.reload }.to change { user.encrypted_password }
end end
it 'sets the new password to expire immediately' do it 'sets the new password to expire immediately' do
update_password(user, 'AValidPassword1') expect { update_password(user, 'AValidPassword1') }
.to change { user.reload.password_expires_at }.to be_within(2.seconds).of(Time.now)
expect { user.reload }.to change { user.password_expires_at }.to(a_value <= Time.now)
end end
end end
...@@ -184,9 +194,8 @@ describe Admin::UsersController do ...@@ -184,9 +194,8 @@ describe Admin::UsersController do
end end
it 'does not update the password' do it 'does not update the password' do
update_password(user, 'invalid') expect { update_password(user, 'invalid') }
.not_to change { user.reload.encrypted_password }
expect { user.reload }.not_to change { user.encrypted_password }
end end
end end
...@@ -204,9 +213,8 @@ describe Admin::UsersController do ...@@ -204,9 +213,8 @@ describe Admin::UsersController do
end end
it 'does not update the password' do it 'does not update the password' do
update_password(user, 'AValidPassword1', 'AValidPassword2') expect { update_password(user, 'AValidPassword1', 'AValidPassword2') }
.not_to change { user.reload.encrypted_password }
expect { user.reload }.not_to change { user.encrypted_password }
end end
end end
end end
......
...@@ -8,34 +8,43 @@ describe ApplicationController do ...@@ -8,34 +8,43 @@ describe ApplicationController do
it 'redirects if the user is over their password expiry' do it 'redirects if the user is over their password expiry' do
user.password_expires_at = Time.new(2002) user.password_expires_at = Time.new(2002)
expect(user.ldap_user?).to be_falsey expect(user.ldap_user?).to be_falsey
allow(controller).to receive(:current_user).and_return(user) allow(controller).to receive(:current_user).and_return(user)
expect(controller).to receive(:redirect_to) expect(controller).to receive(:redirect_to)
expect(controller).to receive(:new_profile_password_path) expect(controller).to receive(:new_profile_password_path)
controller.send(:check_password_expiration) controller.send(:check_password_expiration)
end end
it 'does not redirect if the user is under their password expiry' do it 'does not redirect if the user is under their password expiry' do
user.password_expires_at = Time.now + 20010101 user.password_expires_at = Time.now + 20010101
expect(user.ldap_user?).to be_falsey expect(user.ldap_user?).to be_falsey
allow(controller).to receive(:current_user).and_return(user) allow(controller).to receive(:current_user).and_return(user)
expect(controller).not_to receive(:redirect_to) expect(controller).not_to receive(:redirect_to)
controller.send(:check_password_expiration) controller.send(:check_password_expiration)
end end
it 'does not redirect if the user is over their password expiry but they are an ldap user' do it 'does not redirect if the user is over their password expiry but they are an ldap user' do
user.password_expires_at = Time.new(2002) user.password_expires_at = Time.new(2002)
allow(user).to receive(:ldap_user?).and_return(true) allow(user).to receive(:ldap_user?).and_return(true)
allow(controller).to receive(:current_user).and_return(user) allow(controller).to receive(:current_user).and_return(user)
expect(controller).not_to receive(:redirect_to) expect(controller).not_to receive(:redirect_to)
controller.send(:check_password_expiration) controller.send(:check_password_expiration)
end end
it 'does not redirect if the user is over their password expiry but sign-in is disabled' do it 'redirects if the user is over their password expiry and sign-in is disabled' do
stub_application_setting(password_authentication_enabled: false) stub_application_setting(password_authentication_enabled: false)
user.password_expires_at = Time.new(2002) user.password_expires_at = Time.new(2002)
expect(user.ldap_user?).to be_falsey
allow(controller).to receive(:current_user).and_return(user) allow(controller).to receive(:current_user).and_return(user)
expect(controller).not_to receive(:redirect_to) expect(controller).to receive(:redirect_to)
expect(controller).to receive(:new_profile_password_path)
controller.send(:check_password_expiration) controller.send(:check_password_expiration)
end end
......
require 'spec_helper' require 'spec_helper'
describe PasswordsController do describe PasswordsController do
describe '#check_password_authentication_available' do describe '#prevent_ldap_reset' do
before do before do
@request.env["devise.mapping"] = Devise.mappings[:user] @request.env["devise.mapping"] = Devise.mappings[:user]
end end
context 'when password authentication is disabled' do context 'when password authentication is disabled' do
it 'prevents a password reset' do it 'allows password reset' do
stub_application_setting(password_authentication_enabled: false) stub_application_setting(password_authentication_enabled: false)
post :create post :create
expect(flash[:alert]).to eq 'Password authentication is unavailable.' expect(response).to have_http_status(302)
end end
end end
...@@ -22,7 +22,7 @@ describe PasswordsController do ...@@ -22,7 +22,7 @@ describe PasswordsController do
it 'prevents a password reset' do it 'prevents a password reset' do
post :create, user: { email: user.email } post :create, user: { email: user.email }
expect(flash[:alert]).to eq 'Password authentication is unavailable.' expect(flash[:alert]).to eq('Cannot reset password for LDAP user.')
end end
end end
end end
......
...@@ -2,7 +2,7 @@ require 'spec_helper' ...@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::PagesController do describe Projects::PagesController do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :public, :access_requestable) } let(:project) { create(:project, :public) }
let(:request_params) do let(:request_params) do
{ {
...@@ -23,6 +23,17 @@ describe Projects::PagesController do ...@@ -23,6 +23,17 @@ describe Projects::PagesController do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
context 'when the project is in a subgroup' do
let(:group) { create(:group, :nested) }
let(:project) { create(:project, namespace: group) }
it 'returns a 404 status code' do
get :show, request_params
expect(response).to have_http_status(404)
end
end
end end
describe 'DELETE destroy' do describe 'DELETE destroy' do
......
...@@ -53,12 +53,12 @@ describe 'Profile > Password' do ...@@ -53,12 +53,12 @@ describe 'Profile > Password' do
context 'Regular user' do context 'Regular user' do
let(:user) { create(:user) } let(:user) { create(:user) }
it 'renders 404 when sign-in is disabled' do it 'renders 200 when sign-in is disabled' do
stub_application_setting(password_authentication_enabled: false) stub_application_setting(password_authentication_enabled: false)
visit edit_profile_password_path visit edit_profile_password_path
expect(page).to have_http_status(404) expect(page).to have_http_status(200)
end end
end end
......
...@@ -4,7 +4,7 @@ describe VersionCheckHelper do ...@@ -4,7 +4,7 @@ describe VersionCheckHelper do
describe '#version_status_badge' do describe '#version_status_badge' do
it 'should return nil if not dev environment and not enabled' do it 'should return nil if not dev environment and not enabled' do
allow(Rails.env).to receive(:production?) { false } allow(Rails.env).to receive(:production?) { false }
allow(current_application_settings).to receive(:version_check_enabled) { false } allow(helper.current_application_settings).to receive(:version_check_enabled) { false }
expect(helper.version_status_badge).to be(nil) expect(helper.version_status_badge).to be(nil)
end end
...@@ -12,7 +12,7 @@ describe VersionCheckHelper do ...@@ -12,7 +12,7 @@ describe VersionCheckHelper do
context 'when production and enabled' do context 'when production and enabled' do
before do before do
allow(Rails.env).to receive(:production?) { true } allow(Rails.env).to receive(:production?) { true }
allow(current_application_settings).to receive(:version_check_enabled) { true } allow(helper.current_application_settings).to receive(:version_check_enabled) { true }
allow_any_instance_of(VersionCheck).to receive(:url) { 'https://version.host.com/check.svg?gitlab_info=xxx' } allow_any_instance_of(VersionCheck).to receive(:url) { 'https://version.host.com/check.svg?gitlab_info=xxx' }
@image_tag = helper.version_status_badge @image_tag = helper.version_status_badge
......
...@@ -76,6 +76,87 @@ import '~/lib/utils/pretty_time'; ...@@ -76,6 +76,87 @@ import '~/lib/utils/pretty_time';
expect(aboveOneWeek.days).toBe(3); expect(aboveOneWeek.days).toBe(3);
expect(aboveOneWeek.weeks).toBe(173); expect(aboveOneWeek.weeks).toBe(173);
}); });
it('should correctly accept a custom param for hoursPerDay', function () {
const parser = prettyTime.parseSeconds;
const config = { hoursPerDay: 24 };
const aboveOneHour = parser(4800, config);
expect(aboveOneHour.minutes).toBe(20);
expect(aboveOneHour.hours).toBe(1);
expect(aboveOneHour.days).toBe(0);
expect(aboveOneHour.weeks).toBe(0);
const aboveOneDay = parser(110000, config);
expect(aboveOneDay.minutes).toBe(33);
expect(aboveOneDay.hours).toBe(6);
expect(aboveOneDay.days).toBe(1);
expect(aboveOneDay.weeks).toBe(0);
const aboveOneWeek = parser(25000000, config);
expect(aboveOneWeek.minutes).toBe(26);
expect(aboveOneWeek.hours).toBe(8);
expect(aboveOneWeek.days).toBe(4);
expect(aboveOneWeek.weeks).toBe(57);
});
it('should correctly accept a custom param for daysPerWeek', function () {
const parser = prettyTime.parseSeconds;
const config = { daysPerWeek: 7 };
const aboveOneHour = parser(4800, config);
expect(aboveOneHour.minutes).toBe(20);
expect(aboveOneHour.hours).toBe(1);
expect(aboveOneHour.days).toBe(0);
expect(aboveOneHour.weeks).toBe(0);
const aboveOneDay = parser(110000, config);
expect(aboveOneDay.minutes).toBe(33);
expect(aboveOneDay.hours).toBe(6);
expect(aboveOneDay.days).toBe(3);
expect(aboveOneDay.weeks).toBe(0);
const aboveOneWeek = parser(25000000, config);
expect(aboveOneWeek.minutes).toBe(26);
expect(aboveOneWeek.hours).toBe(0);
expect(aboveOneWeek.days).toBe(0);
expect(aboveOneWeek.weeks).toBe(124);
});
it('should correctly accept custom params for daysPerWeek and hoursPerDay', function () {
const parser = prettyTime.parseSeconds;
const config = { daysPerWeek: 55, hoursPerDay: 14 };
const aboveOneHour = parser(4800, config);
expect(aboveOneHour.minutes).toBe(20);
expect(aboveOneHour.hours).toBe(1);
expect(aboveOneHour.days).toBe(0);
expect(aboveOneHour.weeks).toBe(0);
const aboveOneDay = parser(110000, config);
expect(aboveOneDay.minutes).toBe(33);
expect(aboveOneDay.hours).toBe(2);
expect(aboveOneDay.days).toBe(2);
expect(aboveOneDay.weeks).toBe(0);
const aboveOneWeek = parser(25000000, config);
expect(aboveOneWeek.minutes).toBe(26);
expect(aboveOneWeek.hours).toBe(0);
expect(aboveOneWeek.days).toBe(1);
expect(aboveOneWeek.weeks).toBe(9);
});
}); });
describe('stringifyTime', function () { describe('stringifyTime', function () {
......
...@@ -41,7 +41,7 @@ describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state do ...@@ -41,7 +41,7 @@ describe Gitlab::Auth::UniqueIpsLimiter, :clean_gitlab_redis_shared_state do
context 'allow 2 unique ips' do context 'allow 2 unique ips' do
before do before do
current_application_settings.update!(unique_ips_limit_per_user: 2) Gitlab::CurrentSettings.current_application_settings.update!(unique_ips_limit_per_user: 2)
end end
it 'blocks user trying to login from third ip' do it 'blocks user trying to login from third ip' do
......
...@@ -279,16 +279,6 @@ describe Gitlab::Auth do ...@@ -279,16 +279,6 @@ describe Gitlab::Auth do
gl_auth.find_with_user_password('ldap_user', 'password') gl_auth.find_with_user_password('ldap_user', 'password')
end end
end end
context "with sign-in disabled" do
before do
stub_application_setting(password_authentication_enabled: false)
end
it "does not find user by valid login/password" do
expect(gl_auth.find_with_user_password(username, password)).to be_nil
end
end
end end
private private
......
require 'spec_helper'
describe Gitlab::ReferenceCounter do
let(:redis) { double('redis') }
let(:reference_counter_key) { "git-receive-pack-reference-counter:project-1" }
let(:reference_counter) { described_class.new('project-1') }
before do
allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis)
end
it 'increases and set the expire time of a reference count for a path' do
expect(redis).to receive(:incr).with(reference_counter_key)
expect(redis).to receive(:expire).with(reference_counter_key,
described_class::REFERENCE_EXPIRE_TIME)
expect(reference_counter.increase).to be(true)
end
it 'decreases the reference count for a path' do
allow(redis).to receive(:decr).and_return(0)
expect(redis).to receive(:decr).with(reference_counter_key)
expect(reference_counter.decrease).to be(true)
end
it 'warns if attempting to decrease a counter with a value of one or less, and resets the counter' do
expect(redis).to receive(:decr).and_return(-1)
expect(redis).to receive(:del)
expect(Rails.logger).to receive(:warn).with("Reference counter for project-1" \
" decreased when its value was less than 1. Reseting the counter.")
expect(reference_counter.decrease).to be(true)
end
it 'get the reference count for a path' do
allow(redis).to receive(:get).and_return(1)
expect(reference_counter.value).to be(1)
end
end
...@@ -228,7 +228,6 @@ describe Gitlab::Workhorse do ...@@ -228,7 +228,6 @@ describe Gitlab::Workhorse do
let(:action) { 'git_upload_pack' } let(:action) { 'git_upload_pack' }
let(:feature_flag) { :post_upload_pack } let(:feature_flag) { :post_upload_pack }
context 'when action is enabled by feature flag' do
it 'includes Gitaly params in the returned value' do it 'includes Gitaly params in the returned value' do
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true) allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag).and_return(true)
...@@ -236,16 +235,6 @@ describe Gitlab::Workhorse do ...@@ -236,16 +235,6 @@ describe Gitlab::Workhorse do
end end
end end
context 'when action is not enabled by feature flag' do
it 'does not include Gitaly params in the returned value' do
status_opt_out = Gitlab::GitalyClient::MigrationStatus::OPT_OUT
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(feature_flag, status: status_opt_out).and_return(false)
expect(subject).not_to include(gitaly_params)
end
end
end
context "when git_receive_pack action is passed" do context "when git_receive_pack action is passed" do
let(:action) { 'git_receive_pack' } let(:action) { 'git_receive_pack' }
......
...@@ -195,6 +195,67 @@ eos ...@@ -195,6 +195,67 @@ eos
it { expect(data[:removed]).to eq([]) } it { expect(data[:removed]).to eq([]) }
end end
describe '#cherry_pick_message' do
let(:user) { create(:user) }
context 'of a regular commit' do
let(:commit) { project.commit('video') }
it { expect(commit.cherry_pick_message(user)).to include("\n\n(cherry picked from commit 88790590ed1337ab189bccaa355f068481c90bec)") }
end
context 'of a merge commit' do
let(:repository) { project.repository }
let(:commit_options) do
author = repository.user_to_committer(user)
{ message: 'Test message', committer: author, author: author }
end
let(:merge_request) do
create(:merge_request,
source_branch: 'video',
target_branch: 'master',
source_project: project,
author: user)
end
let(:merge_commit) do
merge_commit_id = repository.merge(user,
merge_request.diff_head_sha,
merge_request,
commit_options)
repository.commit(merge_commit_id)
end
context 'that is found' do
before do
# Artificially mark as completed.
merge_request.update(merge_commit_sha: merge_commit.id)
end
it do
expected_appended_text = <<~STR.rstrip
(cherry picked from commit #{merge_commit.sha})
467dc98f Add new 'videos' directory
88790590 Upload new video file
STR
expect(merge_commit.cherry_pick_message(user)).to include(expected_appended_text)
end
end
context "that is existing but not found" do
it 'does not include details of the merged commits' do
expect(merge_commit.cherry_pick_message(user)).to end_with("(cherry picked from commit #{merge_commit.sha})")
end
end
end
end
describe '#reverts_commit?' do describe '#reverts_commit?' do
let(:another_commit) { double(:commit, revert_description: "This reverts commit #{commit.sha}") } let(:another_commit) { double(:commit, revert_description: "This reverts commit #{commit.sha}") }
let(:user) { commit.author } let(:user) { commit.author }
......
...@@ -790,4 +790,22 @@ describe Issue do ...@@ -790,4 +790,22 @@ describe Issue do
expect(described_class.public_only).to eq([public_issue]) expect(described_class.public_only).to eq([public_issue])
end end
end end
describe '#update_project_counter_caches?' do
it 'returns true when the state changes' do
subject.state = 'closed'
expect(subject.update_project_counter_caches?).to eq(true)
end
it 'returns true when the confidential flag changes' do
subject.confidential = true
expect(subject.update_project_counter_caches?).to eq(true)
end
it 'returns false when the state or confidential flag did not change' do
expect(subject.update_project_counter_caches?).to eq(false)
end
end
end end
...@@ -159,6 +159,7 @@ describe MergeRequest do ...@@ -159,6 +159,7 @@ describe MergeRequest do
before do before do
subject.project.has_external_issue_tracker = true subject.project.has_external_issue_tracker = true
subject.project.save! subject.project.save!
create(:jira_service, project: subject.project)
end end
it 'does not cache issues from external trackers' do it 'does not cache issues from external trackers' do
...@@ -167,6 +168,7 @@ describe MergeRequest do ...@@ -167,6 +168,7 @@ describe MergeRequest do
allow(subject).to receive(:commits).and_return([commit]) allow(subject).to receive(:commits).and_return([commit])
expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to raise_error
expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count) expect { subject.cache_merge_request_closes_issues!(subject.author) }.not_to change(subject.merge_requests_closing_issues, :count)
end end
...@@ -2187,4 +2189,16 @@ describe MergeRequest do ...@@ -2187,4 +2189,16 @@ describe MergeRequest do
.to change { project.open_merge_requests_count }.from(1).to(0) .to change { project.open_merge_requests_count }.from(1).to(0)
end end
end end
describe '#update_project_counter_caches?' do
it 'returns true when the state changes' do
subject.state = 'closed'
expect(subject.update_project_counter_caches?).to eq(true)
end
it 'returns false when the state did not change' do
expect(subject.update_project_counter_caches?).to eq(false)
end
end
end end
...@@ -2713,6 +2713,28 @@ describe Project do ...@@ -2713,6 +2713,28 @@ describe Project do
end end
end end
describe '#pages_available?' do
let(:project) { create(:project, group: group) }
subject { project.pages_available? }
before do
allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
end
context 'when the project is in a top level namespace' do
let(:group) { create(:group) }
it { is_expected.to be(true) }
end
context 'when the project is in a subgroup' do
let(:group) { create(:group, :nested) }
it { is_expected.to be(false) }
end
end
describe '#remove_private_deploy_keys' do describe '#remove_private_deploy_keys' do
let!(:project) { create(:project) } let!(:project) { create(:project) }
......
...@@ -1413,8 +1413,11 @@ describe Repository, models: true do ...@@ -1413,8 +1413,11 @@ describe Repository, models: true do
it 'cherry-picks the changes' do it 'cherry-picks the changes' do
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).to be_nil
repository.cherry_pick(user, pickable_merge, 'improve/awesome') cherry_pick_commit_sha = repository.cherry_pick(user, pickable_merge, 'improve/awesome')
cherry_pick_commit_message = project.commit(cherry_pick_commit_sha).message
expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil expect(repository.blob_at_branch('improve/awesome', 'foo/bar/.gitkeep')).not_to be_nil
expect(cherry_pick_commit_message).to include('cherry picked from')
end end
end end
end end
......
...@@ -281,6 +281,12 @@ describe WikiPage do ...@@ -281,6 +281,12 @@ describe WikiPage do
@page.title = "Import-existing-repositories-into-GitLab" @page.title = "Import-existing-repositories-into-GitLab"
expect(@page.title).to eq("Import existing repositories into GitLab") expect(@page.title).to eq("Import existing repositories into GitLab")
end end
it 'unescapes html' do
@page.title = 'foo &amp; bar'
expect(@page.title).to eq('foo & bar')
end
end end
describe '#directory' do describe '#directory' do
......
...@@ -804,7 +804,7 @@ describe API::Commits do ...@@ -804,7 +804,7 @@ describe API::Commits do
expect(response).to have_gitlab_http_status(201) expect(response).to have_gitlab_http_status(201)
expect(response).to match_response_schema('public_api/v4/commit/basic') expect(response).to match_response_schema('public_api/v4/commit/basic')
expect(json_response['title']).to eq(commit.title) expect(json_response['title']).to eq(commit.title)
expect(json_response['message']).to eq(commit.message) expect(json_response['message']).to eq(commit.cherry_pick_message(user))
expect(json_response['author_name']).to eq(commit.author_name) expect(json_response['author_name']).to eq(commit.author_name)
expect(json_response['committer_name']).to eq(user.name) expect(json_response['committer_name']).to eq(user.name)
end end
......
...@@ -708,6 +708,95 @@ describe API::Internal do ...@@ -708,6 +708,95 @@ describe API::Internal do
# end # end
# end # end
describe 'POST /internal/post_receive' do
let(:gl_repository) { "project-#{project.id}" }
let(:identifier) { 'key-123' }
let(:reference_counter) { double('ReferenceCounter') }
let(:valid_params) do
{
gl_repository: gl_repository,
secret_token: secret_token,
identifier: identifier,
changes: changes
}
end
let(:changes) do
"#{Gitlab::Git::BLANK_SHA} 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/new_branch"
end
before do
project.team << [user, :developer]
end
it 'enqueues a PostReceive worker job' do
expect(PostReceive).to receive(:perform_async)
.with(gl_repository, identifier, changes)
post api("/internal/post_receive"), valid_params
end
it 'decreases the reference counter and returns the result' do
expect(Gitlab::ReferenceCounter).to receive(:new).with(gl_repository)
.and_return(reference_counter)
expect(reference_counter).to receive(:decrease).and_return(true)
post api("/internal/post_receive"), valid_params
expect(json_response['reference_counter_decreased']).to be(true)
end
it 'returns link to create new merge request' do
post api("/internal/post_receive"), valid_params
expect(json_response['merge_request_urls']).to match [{
"branch_name" => "new_branch",
"url" => "http://#{Gitlab.config.gitlab.host}/#{project.namespace.name}/#{project.path}/merge_requests/new?merge_request%5Bsource_branch%5D=new_branch",
"new_merge_request" => true
}]
end
it 'returns empty array if printing_merge_request_link_enabled is false' do
project.update!(printing_merge_request_link_enabled: false)
post api("/internal/post_receive"), valid_params
expect(json_response['merge_request_urls']).to eq([])
end
context 'broadcast message exists' do
let!(:broadcast_message) { create(:broadcast_message, starts_at: 1.day.ago, ends_at: 1.day.from_now ) }
it 'returns one broadcast message' do
post api("/internal/post_receive"), valid_params
expect(response).to have_http_status(200)
expect(json_response['broadcast_message']).to eq(broadcast_message.message)
end
end
context 'broadcast message does not exist' do
it 'returns empty string' do
post api("/internal/post_receive"), valid_params
expect(response).to have_http_status(200)
expect(json_response['broadcast_message']).to eq(nil)
end
end
context 'nil broadcast message' do
it 'returns empty string' do
allow(BroadcastMessage).to receive(:current).and_return(nil)
post api("/internal/post_receive"), valid_params
expect(response).to have_http_status(200)
expect(json_response['broadcast_message']).to eq(nil)
end
end
end
def project_with_repo_path(path) def project_with_repo_path(path)
double().tap do |fake_project| double().tap do |fake_project|
allow(fake_project).to receive_message_chain('repository.path_to_repo' => path) allow(fake_project).to receive_message_chain('repository.path_to_repo' => path)
......
...@@ -474,7 +474,7 @@ describe API::V3::Commits do ...@@ -474,7 +474,7 @@ describe API::V3::Commits do
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
expect(json_response['title']).to eq(master_pickable_commit.title) expect(json_response['title']).to eq(master_pickable_commit.title)
expect(json_response['message']).to eq(master_pickable_commit.message) expect(json_response['message']).to eq(master_pickable_commit.cherry_pick_message(user))
expect(json_response['author_name']).to eq(master_pickable_commit.author_name) expect(json_response['author_name']).to eq(master_pickable_commit.author_name)
expect(json_response['committer_name']).to eq(user.name) expect(json_response['committer_name']).to eq(user.name)
end end
......
...@@ -1244,5 +1244,15 @@ describe QuickActions::InterpretService do ...@@ -1244,5 +1244,15 @@ describe QuickActions::InterpretService do
expect(explanations).to eq(['Sets weight to 4.']) expect(explanations).to eq(['Sets weight to 4.'])
end end
end end
describe 'move issue to another project command' do
let(:content) { '/move test/project' }
it 'includes the project name' do
_, explanations = service.explain(content, issue)
expect(explanations).to eq(["Moves this issue to test/project."])
end
end
end end
end end
# Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb # Inspired by https://github.com/ljkbennett/stub_env/blob/master/lib/stub_env/helpers.rb
module StubENV module StubENV
include Gitlab::CurrentSettings
def stub_env(key_or_hash, value = nil) def stub_env(key_or_hash, value = nil)
init_stub unless env_stubbed? init_stub unless env_stubbed?
if key_or_hash.is_a? Hash if key_or_hash.is_a? Hash
......
...@@ -9,6 +9,7 @@ describe 'admin/dashboard/index.html.haml' do ...@@ -9,6 +9,7 @@ describe 'admin/dashboard/index.html.haml' do
assign(:groups, create_list(:group, 1)) assign(:groups, create_list(:group, 1))
allow(view).to receive(:admin?).and_return(true) allow(view).to receive(:admin?).and_return(true)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end end
it "shows version of GitLab Workhorse" do it "shows version of GitLab Workhorse" do
......
...@@ -5,6 +5,7 @@ describe 'devise/shared/_signin_box' do ...@@ -5,6 +5,7 @@ describe 'devise/shared/_signin_box' do
before do before do
stub_devise stub_devise
assign(:ldap_servers, []) assign(:ldap_servers, [])
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end end
it 'is shown when Crowd is enabled' do it 'is shown when Crowd is enabled' do
......
...@@ -37,5 +37,6 @@ describe 'help/index' do ...@@ -37,5 +37,6 @@ describe 'help/index' do
def stub_helpers def stub_helpers
allow(view).to receive(:markdown).and_return('') allow(view).to receive(:markdown).and_return('')
allow(view).to receive(:version_status_badge).and_return('') allow(view).to receive(:version_status_badge).and_return('')
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end end
end end
require 'spec_helper' require 'spec_helper'
describe 'layouts/_head' do describe 'layouts/_head' do
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
it 'escapes HTML-safe strings in page_title' do it 'escapes HTML-safe strings in page_title' do
stub_helper_with_safe_string(:page_title) stub_helper_with_safe_string(:page_title)
......
require 'spec_helper' require 'spec_helper'
describe 'projects/commits/_commit.html.haml' do describe 'projects/commits/_commit.html.haml' do
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
context 'with a singed commit' do context 'with a singed commit' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:repository) { project.repository } let(:repository) { project.repository }
......
...@@ -10,7 +10,9 @@ describe 'projects/edit' do ...@@ -10,7 +10,9 @@ describe 'projects/edit' do
assign(:project, project) assign(:project, project)
allow(controller).to receive(:current_user).and_return(user) allow(controller).to receive(:current_user).and_return(user)
allow(view).to receive_messages(current_user: user, can?: true) allow(view).to receive_messages(current_user: user,
can?: true,
current_application_settings: Gitlab::CurrentSettings.current_application_settings)
end end
context 'LFS enabled setting' do context 'LFS enabled setting' do
......
...@@ -14,6 +14,7 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do ...@@ -14,6 +14,7 @@ describe 'projects/merge_requests/creations/_new_submit.html.haml' do
allow(view).to receive(:can?).and_return(true) allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:url_for).and_return('#') allow(view).to receive(:url_for).and_return('#')
allow(view).to receive(:current_user).and_return(merge_request.author) allow(view).to receive(:current_user).and_return(merge_request.author)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end end
context 'when there are pipelines for merge request but no pipeline for last commit' do context 'when there are pipelines for merge request but no pipeline for last commit' do
......
...@@ -25,7 +25,9 @@ describe 'projects/merge_requests/show.html.haml' do ...@@ -25,7 +25,9 @@ describe 'projects/merge_requests/show.html.haml' do
assign(:notes, []) assign(:notes, [])
assign(:pipelines, Ci::Pipeline.none) assign(:pipelines, Ci::Pipeline.none)
allow(view).to receive_messages(current_user: user, can?: true) allow(view).to receive_messages(current_user: user,
can?: true,
current_application_settings: Gitlab::CurrentSettings.current_application_settings)
end end
context 'when the merge request is closed' do context 'when the merge request is closed' do
......
...@@ -12,6 +12,7 @@ describe 'projects/tree/show' do ...@@ -12,6 +12,7 @@ describe 'projects/tree/show' do
allow(view).to receive(:can?).and_return(true) allow(view).to receive(:can?).and_return(true)
allow(view).to receive(:can_collaborate_with_project?).and_return(true) allow(view).to receive(:can_collaborate_with_project?).and_return(true)
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end end
context 'for branch names ending on .json' do context 'for branch names ending on .json' do
......
...@@ -3,6 +3,10 @@ require 'spec_helper' ...@@ -3,6 +3,10 @@ require 'spec_helper'
describe 'shared/projects/_project.html.haml' do describe 'shared/projects/_project.html.haml' do
let(:project) { create(:project) } let(:project) { create(:project) }
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
it 'should render creator avatar if project has a creator' do it 'should render creator avatar if project has a creator' do
render 'shared/projects/project', use_creator_avatar: true, project: project render 'shared/projects/project', use_creator_avatar: true, project: project
......
...@@ -990,7 +990,7 @@ brace-expansion@^1.0.0: ...@@ -990,7 +990,7 @@ brace-expansion@^1.0.0:
balanced-match "^0.4.1" balanced-match "^0.4.1"
concat-map "0.0.1" concat-map "0.0.1"
brace-expansion@^1.1.7: brace-expansion@^1.1.8:
version "1.1.8" version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
dependencies: dependencies:
......
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