summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
Diffstat (limited to 'app/models')
-rw-r--r--app/models/ability.rb29
-rw-r--r--app/models/application_setting.rb3
-rw-r--r--app/models/ci/build.rb41
-rw-r--r--app/models/ci/commit.rb11
-rw-r--r--app/models/commit.rb18
-rw-r--r--app/models/commit_status.rb25
-rw-r--r--app/models/concerns/internal_id.rb10
-rw-r--r--app/models/concerns/issuable.rb9
-rw-r--r--app/models/concerns/statuseable.rb (renamed from app/models/concerns/ci_status.rb)8
-rw-r--r--app/models/event.rb2
-rw-r--r--app/models/external_issue.rb6
-rw-r--r--app/models/group.rb1
-rw-r--r--app/models/hooks/project_hook.rb3
-rw-r--r--app/models/hooks/system_hook.rb3
-rw-r--r--app/models/hooks/web_hook.rb3
-rw-r--r--app/models/issue.rb48
-rw-r--r--app/models/label.rb4
-rw-r--r--app/models/merge_request.rb7
-rw-r--r--app/models/merge_request_diff.rb2
-rw-r--r--app/models/note.rb1
-rw-r--r--app/models/oauth_access_token.rb19
-rw-r--r--app/models/project.rb63
-rw-r--r--app/models/project_import_data.rb15
-rw-r--r--app/models/project_services/bamboo_service.rb20
-rw-r--r--app/models/project_services/builds_email_service.rb10
-rw-r--r--app/models/project_services/hipchat_service.rb4
-rw-r--r--app/models/project_services/slack_service.rb5
-rw-r--r--app/models/project_services/slack_service/merge_message.rb2
-rw-r--r--app/models/project_services/slack_service/note_message.rb2
-rw-r--r--app/models/project_services/slack_service/wiki_page_message.rb53
-rw-r--r--app/models/project_services/teamcity_service.rb33
-rw-r--r--app/models/repository.rb99
-rw-r--r--app/models/service.rb4
-rw-r--r--app/models/user.rb1
-rw-r--r--app/models/wiki_page.rb4
35 files changed, 431 insertions, 137 deletions
diff --git a/app/models/ability.rb b/app/models/ability.rb
index ec5ac54c277..d3e8a3fa79a 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -18,6 +18,7 @@ class Ability
when Namespace then namespace_abilities(user, subject)
when GroupMember then group_member_abilities(user, subject)
when ProjectMember then project_member_abilities(user, subject)
+ when User then user_abilities
else []
end.concat(global_abilities(user))
end
@@ -35,6 +36,8 @@ class Ability
anonymous_project_abilities(subject)
when subject.is_a?(Group) || subject.respond_to?(:group)
anonymous_group_abilities(subject)
+ when subject.is_a?(User)
+ anonymous_user_abilities
else
[]
end
@@ -81,17 +84,17 @@ class Ability
end
def anonymous_group_abilities(subject)
+ rules = []
+
group = if subject.is_a?(Group)
subject
else
subject.group
end
- if group && group.public?
- [:read_group]
- else
- []
- end
+ rules << :read_group if group.public?
+
+ rules
end
def anonymous_personal_snippet_abilities(snippet)
@@ -110,9 +113,14 @@ class Ability
end
end
+ def anonymous_user_abilities
+ [:read_user] unless restricted_public_level?
+ end
+
def global_abilities(user)
rules = []
rules << :create_group if user.can_create_group
+ rules << :read_users_list
rules
end
@@ -163,7 +171,7 @@ class Ability
@public_project_rules ||= project_guest_rules + [
:download_code,
:fork_project,
- :read_commit_status,
+ :read_commit_status
]
end
@@ -289,7 +297,6 @@ class Ability
def group_abilities(user, group)
rules = []
-
rules << :read_group if can_read_group?(user, group)
# Only group masters and group owners can create new projects
@@ -461,6 +468,10 @@ class Ability
rules
end
+ def user_abilities
+ [:read_user]
+ end
+
def abilities
@abilities ||= begin
abilities = Six.new
@@ -475,6 +486,10 @@ class Ability
private
+ def restricted_public_level?
+ current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
+ end
+
def named_abilities(name)
[
:"read_#{name}",
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 052cd874733..36f88154232 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -153,7 +153,8 @@ class ApplicationSetting < ActiveRecord::Base
require_two_factor_authentication: false,
two_factor_grace_period: 48,
recaptcha_enabled: false,
- akismet_enabled: false
+ akismet_enabled: false,
+ repository_checks_enabled: true,
)
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index c0b334d3600..553cd447971 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -224,12 +224,33 @@ module Ci
end
end
+ def trace_length
+ if raw_trace
+ raw_trace.length
+ else
+ 0
+ end
+ end
+
def trace=(trace)
+ recreate_trace_dir
+ File.write(path_to_trace, trace)
+ end
+
+ def recreate_trace_dir
unless Dir.exists?(dir_to_trace)
FileUtils.mkdir_p(dir_to_trace)
end
+ end
+ private :recreate_trace_dir
- File.write(path_to_trace, trace)
+ def append_trace(trace_part, offset)
+ recreate_trace_dir
+
+ File.truncate(path_to_trace, offset) if File.exist?(path_to_trace)
+ File.open(path_to_trace, 'a') do |f|
+ f.write(trace_part)
+ end
end
def dir_to_trace
@@ -359,11 +380,23 @@ module Ci
self.update(erased_by: user, erased_at: Time.now)
end
- private
-
def yaml_variables
+ global_yaml_variables + job_yaml_variables
+ end
+
+ def global_yaml_variables
+ if commit.config_processor
+ commit.config_processor.global_variables.map do |key, value|
+ { key: key, value: value, public: true }
+ end
+ else
+ []
+ end
+ end
+
+ def job_yaml_variables
if commit.config_processor
- commit.config_processor.variables.map do |key, value|
+ commit.config_processor.job_variables(name).map do |key, value|
{ key: key, value: value, public: true }
end
else
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 6abeddeeae6..fadf5c62984 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -19,14 +19,17 @@
module Ci
class Commit < ActiveRecord::Base
extend Ci::Model
- include CiStatus
+ include Statuseable
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
has_many :statuses, class_name: 'CommitStatus'
has_many :builds, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
+ delegate :stages, to: :statuses
+
validates_presence_of :sha
+ validates_presence_of :status
validate :valid_commit_sha
# Invalidate object and save if when touched
@@ -40,10 +43,6 @@ module Ci
CommitStatus.where(commit: all).stages
end
- def stages
- statuses.stages
- end
-
def project_id
project.id
end
@@ -82,7 +81,7 @@ module Ci
def retryable?
builds.latest.any? do |build|
- build.failed? || build.retryable?
+ build.failed? && build.retryable?
end
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 9ffdcc59128..562c3ed15b2 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -15,8 +15,8 @@ class Commit
DIFF_SAFE_LINES = Gitlab::Git::DiffCollection::DEFAULT_LIMITS[:max_lines]
# Commits above this size will not be rendered in HTML
- DIFF_HARD_LIMIT_FILES = 1000 unless defined?(DIFF_HARD_LIMIT_FILES)
- DIFF_HARD_LIMIT_LINES = 50000 unless defined?(DIFF_HARD_LIMIT_LINES)
+ DIFF_HARD_LIMIT_FILES = 1000
+ DIFF_HARD_LIMIT_LINES = 50000
class << self
def decorate(commits, project)
@@ -154,7 +154,7 @@ class Commit
id: id,
message: safe_message,
timestamp: committed_date.xmlschema,
- url: commit_url,
+ url: Gitlab::UrlBuilder.build(self),
author: {
name: author_name,
email: author_email
@@ -168,10 +168,6 @@ class Commit
data
end
- def commit_url
- project.present? ? "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/#{id}" : ""
- end
-
# Discover issues should be closed when this commit is pushed to a project's
# default branch.
def closes_issues(current_user = self.committer)
@@ -223,6 +219,10 @@ class Commit
def revert_branch_name
"revert-#{short_id}"
end
+
+ def cherry_pick_branch_name
+ project.repository.next_branch("cherry-pick-#{short_id}", mild: true)
+ end
def revert_description
if merged_merge_request
@@ -258,6 +258,10 @@ class Commit
end.any? { |commit_ref| commit_ref.reverts_commit?(self) }
end
+ def change_type_title
+ merged_merge_request ? 'merge request' : 'commit'
+ end
+
private
def repo_changes
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 66eb5dcecf9..aa56314aa16 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -33,7 +33,7 @@
#
class CommitStatus < ActiveRecord::Base
- include CiStatus
+ include Statuseable
self.table_name = 'ci_builds'
@@ -81,7 +81,11 @@ class CommitStatus < ActiveRecord::Base
end
end
- delegate :before_sha, :sha, :short_sha, to: :commit, prefix: false
+ delegate :sha, :short_sha, to: :commit
+
+ def before_sha
+ commit.before_sha || Gitlab::Git::BLANK_SHA
+ end
def self.stages
order_by = 'max(stage_idx)'
@@ -89,7 +93,10 @@ class CommitStatus < ActiveRecord::Base
end
def self.stages_status
- Hash[group(:stage).pluck(:stage, self.status_sql)]
+ all.stages.inject({}) do |h, stage|
+ h[stage] = all.where(stage: stage).status
+ h
+ end
end
def ignored?
@@ -97,11 +104,13 @@ class CommitStatus < ActiveRecord::Base
end
def duration
- if started_at && finished_at
- finished_at - started_at
- elsif started_at
- Time.now - started_at
- end
+ duration =
+ if started_at && finished_at
+ finished_at - started_at
+ elsif started_at
+ Time.now - started_at
+ end
+ duration
end
def stuck?
diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb
index 51288094ef1..5382dde6765 100644
--- a/app/models/concerns/internal_id.rb
+++ b/app/models/concerns/internal_id.rb
@@ -7,11 +7,13 @@ module InternalId
end
def set_iid
- records = project.send(self.class.name.tableize)
- records = records.with_deleted if self.paranoid?
- max_iid = records.maximum(:iid)
+ if iid.blank?
+ records = project.send(self.class.name.tableize)
+ records = records.with_deleted if self.paranoid?
+ max_iid = records.maximum(:iid)
- self.iid = max_iid.to_i + 1
+ self.iid = max_iid.to_i + 1
+ end
end
def to_param
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index afa2ca039ae..d5166e81474 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -37,7 +37,6 @@ module Issuable
scope :closed, -> { with_state(:closed) }
scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') }
scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') }
- scope :with_label, ->(title) { joins(:labels).where(labels: { title: title }) }
scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
scope :join_project, -> { joins(:project) }
@@ -122,6 +121,14 @@ module Issuable
joins(join_clause).group(issuable_table[:id]).reorder("COUNT(notes.id) DESC")
end
+
+ def with_label(title)
+ if title.is_a?(Array) && title.count > 1
+ joins(:labels).where(labels: { title: title }).group('issues.id').having("count(distinct labels.title) = #{title.count}")
+ else
+ joins(:labels).where(labels: { title: title })
+ end
+ end
end
def today?
diff --git a/app/models/concerns/ci_status.rb b/app/models/concerns/statuseable.rb
index 8190b2a20c6..8a293b7b76e 100644
--- a/app/models/concerns/ci_status.rb
+++ b/app/models/concerns/statuseable.rb
@@ -1,4 +1,4 @@
-module CiStatus
+module Statuseable
extend ActiveSupport::Concern
AVAILABLE_STATUSES = %w(pending running success failed canceled skipped)
@@ -7,7 +7,7 @@ module CiStatus
def status_sql
builds = all.select('count(*)').to_sql
success = all.success.select('count(*)').to_sql
- ignored = all.ignored.select('count(*)').to_sql if all.try(:ignored)
+ ignored = all.ignored.select('count(*)').to_sql if all.respond_to?(:ignored)
ignored ||= '0'
pending = all.pending.select('count(*)').to_sql
running = all.running.select('count(*)').to_sql
@@ -33,7 +33,7 @@ module CiStatus
def duration
duration_array = all.map(&:duration).compact
- duration_array.reduce(:+).to_i
+ duration_array.reduce(:+)
end
def started_at
@@ -41,7 +41,7 @@ module CiStatus
end
def finished_at
- all.minimum(:finished_at)
+ all.maximum(:finished_at)
end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 12183524b79..897518aadc7 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -345,7 +345,7 @@ class Event < ActiveRecord::Base
end
def reset_project_activity
- if project
+ if project && Gitlab::ExclusiveLease.new("project:update_last_activity_at:#{project.id}", timeout: 60).try_obtain
project.update_column(:last_activity_at, self.created_at)
end
end
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
index b8585d4e577..b7894c99846 100644
--- a/app/models/external_issue.rb
+++ b/app/models/external_issue.rb
@@ -37,4 +37,10 @@ class ExternalIssue
def to_reference(_from_project = nil)
id
end
+
+ def reference_link_text(from_project = nil)
+ return "##{id}" if /^\d+$/.match(id)
+
+ id
+ end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 9a04ac70d35..1f8432e3320 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -15,7 +15,6 @@
#
require 'carrierwave/orm/activerecord'
-require 'file_size_validator'
class Group < Namespace
include Gitlab::ConfigHelper
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index fe923fafbe0..bc6e0f98c3c 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -21,10 +21,9 @@
class ProjectHook < WebHook
belongs_to :project
- scope :push_hooks, -> { where(push_events: true) }
- scope :tag_push_hooks, -> { where(tag_push_events: true) }
scope :issue_hooks, -> { where(issues_events: true) }
scope :note_hooks, -> { where(note_events: true) }
scope :merge_request_hooks, -> { where(merge_requests_events: true) }
scope :build_hooks, -> { where(build_events: true) }
+ scope :wiki_page_hooks, -> { where(wiki_page_events: true) }
end
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index c147d8762a9..15dddcc2447 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -19,4 +19,7 @@
#
class SystemHook < WebHook
+ def async_execute(data, hook_name)
+ Sidekiq::Client.enqueue(SystemHookWorker, id, data, hook_name)
+ end
end
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index 7a13c3f0a39..3a2e4f546f7 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -30,6 +30,9 @@ class WebHook < ActiveRecord::Base
default_value_for :build_events, false
default_value_for :enable_ssl_verification, true
+ scope :push_hooks, -> { where(push_events: true) }
+ scope :tag_push_hooks, -> { where(tag_push_events: true) }
+
# HTTParty timeout
default_timeout Gitlab.config.gitlab.webhook_timeout
diff --git a/app/models/issue.rb b/app/models/issue.rb
index e064b0f8b95..ea1bfb776ee 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -20,7 +20,6 @@
#
require 'carrierwave/orm/activerecord'
-require 'file_size_validator'
class Issue < ActiveRecord::Base
include InternalId
@@ -29,6 +28,13 @@ class Issue < ActiveRecord::Base
include Sortable
include Taskable
+ DueDateStruct = Struct.new(:title, :name).freeze
+ NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
+ AnyDueDate = DueDateStruct.new('Any Due Date', '').freeze
+ Overdue = DueDateStruct.new('Overdue', 'overdue').freeze
+ DueThisWeek = DueDateStruct.new('Due This Week', 'week').freeze
+ DueThisMonth = DueDateStruct.new('Due This Month', 'month').freeze
+
ActsAsTaggableOn.strict_case_match = true
belongs_to :project
@@ -40,6 +46,13 @@ class Issue < ActiveRecord::Base
scope :open_for, ->(user) { opened.assigned_to(user) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids) }
+ scope :without_due_date, -> { where(due_date: nil) }
+ scope :due_before, ->(date) { where('issues.due_date < ?', date) }
+ scope :due_between, ->(from_date, to_date) { where('issues.due_date >= ?', from_date).where('issues.due_date <= ?', to_date) }
+
+ scope :order_due_date_asc, -> { reorder('issues.due_date IS NULL, issues.due_date ASC') }
+ scope :order_due_date_desc, -> { reorder('issues.due_date IS NULL, issues.due_date DESC') }
+
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
@@ -83,6 +96,15 @@ class Issue < ActiveRecord::Base
@link_reference_pattern ||= super("issues", /(?<issue>\d+)/)
end
+ def self.sort(method)
+ case method.to_s
+ when 'due_date_asc' then order_due_date_asc
+ when 'due_date_desc' then order_due_date_desc
+ else
+ super
+ end
+ end
+
def to_reference(from_project = nil)
reference = "#{self.class.reference_prefix}#{iid}"
@@ -104,10 +126,16 @@ class Issue < ActiveRecord::Base
end
end
- def related_branches
- project.repository.branch_names.select do |branch|
- branch.end_with?("-#{iid}")
+ # All branches containing the current issue's ID, except for
+ # those with a merge request open referencing the current issue.
+ def related_branches(current_user)
+ branches_with_iid = project.repository.branch_names.select do |branch|
+ branch =~ /\A#{iid}-(?!\d+-stable)/i
end
+
+ branches_with_merge_request = self.referenced_merge_requests(current_user).map(&:source_branch)
+
+ branches_with_iid - branches_with_merge_request
end
# Reset issue events cache
@@ -151,13 +179,21 @@ class Issue < ActiveRecord::Base
end
def to_branch_name
- "#{title.parameterize}-#{iid}"
+ if self.confidential?
+ "#{iid}-confidential-issue"
+ else
+ "#{iid}-#{title.parameterize}"
+ end
end
def can_be_worked_on?(current_user)
!self.closed? &&
!self.project.forked? &&
- self.related_branches.empty? &&
+ self.related_branches(current_user).empty? &&
self.closed_by_merge_requests(current_user).empty?
end
+
+ def overdue?
+ due_date.try(:past?) || false
+ end
end
diff --git a/app/models/label.rb b/app/models/label.rb
index 55c01cae762..60bdce32952 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -113,6 +113,10 @@ class Label < ActiveRecord::Base
template
end
+ def text_color
+ LabelsHelper::text_color_for_bg(self.color)
+ end
+
private
def label_format_reference(format = :id)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 6766e4d1afa..d00919c3b0c 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -27,9 +27,6 @@
# merge_commit_sha :string
#
-require Rails.root.join("app/models/commit")
-require Rails.root.join("lib/static_model")
-
class MergeRequest < ActiveRecord::Base
include InternalId
include Issuable
@@ -605,4 +602,8 @@ class MergeRequest < ActiveRecord::Base
def can_be_reverted?(current_user = nil)
merge_commit && !merge_commit.has_been_reverted?(current_user, self)
end
+
+ def can_be_cherry_picked?
+ merge_commit
+ end
end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 33884118595..0580cafdd1b 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -11,8 +11,6 @@
# updated_at :datetime
#
-require Rails.root.join("app/models/commit")
-
class MergeRequestDiff < ActiveRecord::Base
include Sortable
diff --git a/app/models/note.rb b/app/models/note.rb
index 87ced65c650..71b4293d57a 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -20,7 +20,6 @@
#
require 'carrierwave/orm/activerecord'
-require 'file_size_validator'
class Note < ActiveRecord::Base
include Gitlab::CurrentSettings
diff --git a/app/models/oauth_access_token.rb b/app/models/oauth_access_token.rb
new file mode 100644
index 00000000000..c78c7f4aa0e
--- /dev/null
+++ b/app/models/oauth_access_token.rb
@@ -0,0 +1,19 @@
+# == Schema Information
+#
+# Table name: oauth_access_tokens
+#
+# id :integer not null, primary key
+# resource_owner_id :integer
+# application_id :integer
+# token :string not null
+# refresh_token :string
+# expires_in :integer
+# revoked_at :datetime
+# created_at :datetime not null
+# scopes :string
+#
+
+class OauthAccessToken < ActiveRecord::Base
+ belongs_to :resource_owner, class_name: 'User'
+ belongs_to :application, class_name: 'Doorkeeper::Application'
+end
diff --git a/app/models/project.rb b/app/models/project.rb
index e48830115bc..0420c6a61ae 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -40,7 +40,6 @@
#
require 'carrierwave/orm/activerecord'
-require 'file_size_validator'
class Project < ActiveRecord::Base
include Gitlab::ConfigHelper
@@ -409,6 +408,35 @@ class Project < ActiveRecord::Base
self.import_data.destroy if self.import_data
end
+ def import_url=(value)
+ import_url = Gitlab::ImportUrl.new(value)
+ create_or_update_import_data(credentials: import_url.credentials)
+ super(import_url.sanitized_url)
+ end
+
+ def import_url
+ if import_data && super
+ import_url = Gitlab::ImportUrl.new(super, credentials: import_data.credentials)
+ import_url.full_url
+ else
+ super
+ end
+ end
+
+ def create_or_update_import_data(data: nil, credentials: nil)
+ project_import_data = import_data || build_import_data
+ if data
+ project_import_data.data ||= {}
+ project_import_data.data = project_import_data.data.merge(data)
+ end
+ if credentials
+ project_import_data.credentials ||= {}
+ project_import_data.credentials = project_import_data.credentials.merge(credentials)
+ end
+
+ project_import_data.save
+ end
+
def import?
external_import? || forked?
end
@@ -792,18 +820,16 @@ class Project < ActiveRecord::Base
wiki = Repository.new("#{old_path}.wiki", self)
if repo.exists?
- repo.expire_cache
- repo.expire_emptiness_caches
+ repo.before_delete
end
if wiki.exists?
- wiki.expire_cache
- wiki.expire_emptiness_caches
+ wiki.before_delete
end
end
- def hook_attrs
- {
+ def hook_attrs(backward: true)
+ attrs = {
name: name,
description: description,
web_url: web_url,
@@ -814,12 +840,19 @@ class Project < ActiveRecord::Base
visibility_level: visibility_level,
path_with_namespace: path_with_namespace,
default_branch: default_branch,
- # Backward compatibility
- homepage: web_url,
- url: url_to_repo,
- ssh_url: ssh_url_to_repo,
- http_url: http_url_to_repo
}
+
+ # Backward compatibility
+ if backward
+ attrs.merge!({
+ homepage: web_url,
+ url: url_to_repo,
+ ssh_url: ssh_url_to_repo,
+ http_url: http_url_to_repo
+ })
+ end
+
+ attrs
end
# Reset events cache related to this project
@@ -865,7 +898,9 @@ class Project < ActiveRecord::Base
def change_head(branch)
repository.before_change_head
- gitlab_shell.update_repository_head(self.path_with_namespace, branch)
+ repository.rugged.references.create('HEAD',
+ "refs/heads/#{branch}",
+ force: true)
reload_default_branch
end
@@ -927,7 +962,7 @@ class Project < ActiveRecord::Base
end
def ci_commit(sha, ref)
- ci_commits.find_by(sha: sha, ref: ref)
+ ci_commits.order(id: :desc).find_by(sha: sha, ref: ref)
end
def ensure_ci_commit(sha, ref)
diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb
index cd3319f077e..2c0ae312f1b 100644
--- a/app/models/project_import_data.rb
+++ b/app/models/project_import_data.rb
@@ -8,12 +8,23 @@
#
require 'carrierwave/orm/activerecord'
-require 'file_size_validator'
class ProjectImportData < ActiveRecord::Base
belongs_to :project
-
+ attr_encrypted :credentials,
+ key: Gitlab::Application.secrets.db_key_base,
+ marshal: true,
+ encode: true,
+ mode: :per_attribute_iv_and_salt
+
serialize :data, JSON
validates :project, presence: true
+
+ before_validation :symbolize_credentials
+
+ def symbolize_credentials
+ # bang doesn't work here - attr_encrypted makes it not to work
+ self.credentials = self.credentials.deep_symbolize_keys unless self.credentials.blank?
+ end
end
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index 9e7f642180e..060062aaf7a 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -82,17 +82,17 @@ class BambooService < CiService
end
def build_info(sha)
- url = URI.parse("#{bamboo_url}/rest/api/latest/result?label=#{sha}")
+ url = URI.join(bamboo_url, "/rest/api/latest/result?label=#{sha}").to_s
if username.blank? && password.blank?
- @response = HTTParty.get(parsed_url.to_s, verify: false)
+ @response = HTTParty.get(url, verify: false)
else
- get_url = "#{url}&os_authType=basic"
+ url << '&os_authType=basic'
auth = {
- username: username,
- password: password,
+ username: username,
+ password: password
}
- @response = HTTParty.get(get_url, verify: false, basic_auth: auth)
+ @response = HTTParty.get(url, verify: false, basic_auth: auth)
end
end
@@ -101,11 +101,11 @@ class BambooService < CiService
if @response.code != 200 || @response['results']['results']['size'] == '0'
# If actual build link can't be determined, send user to build summary page.
- "#{bamboo_url}/browse/#{build_key}"
+ URI.join(bamboo_url, "/browse/#{build_key}").to_s
else
# If actual build link is available, go to build result page.
result_key = @response['results']['results']['result']['planResultKey']['key']
- "#{bamboo_url}/browse/#{result_key}"
+ URI.join(bamboo_url, "/browse/#{result_key}").to_s
end
end
@@ -134,7 +134,7 @@ class BambooService < CiService
return unless supported_events.include?(data[:object_kind])
# Bamboo requires a GET and does not take any data.
- self.class.get("#{bamboo_url}/updateAndBuild.action?buildKey=#{build_key}",
- verify: false)
+ url = URI.join(bamboo_url, "/updateAndBuild.action?buildKey=#{build_key}").to_s
+ self.class.get(url, verify: false)
end
end
diff --git a/app/models/project_services/builds_email_service.rb b/app/models/project_services/builds_email_service.rb
index f9f04838766..6ab6d7417b7 100644
--- a/app/models/project_services/builds_email_service.rb
+++ b/app/models/project_services/builds_email_service.rb
@@ -23,7 +23,7 @@ class BuildsEmailService < Service
prop_accessor :recipients
boolean_accessor :add_pusher
boolean_accessor :notify_only_broken_builds
- validates :recipients, presence: true, if: :activated?
+ validates :recipients, presence: true, if: ->(s) { s.activated? && !s.add_pusher? }
def initialize_properties
if properties.nil?
@@ -87,10 +87,14 @@ class BuildsEmailService < Service
end
def all_recipients(data)
- all_recipients = recipients.split(',').compact.reject(&:blank?)
+ all_recipients = []
+
+ unless recipients.blank?
+ all_recipients += recipients.split(',').compact.reject(&:blank?)
+ end
if add_pusher? && data[:user][:email]
- all_recipients << "#{data[:user][:email]}"
+ all_recipients << data[:user][:email]
end
all_recipients
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 0e3fa4a40fe..064ef8e8674 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -183,7 +183,7 @@ class HipchatService < Service
title = obj_attr[:title]
merge_request_url = "#{project_url}/merge_requests/#{merge_request_id}"
- merge_request_link = "<a href=\"#{merge_request_url}\">merge request ##{merge_request_id}</a>"
+ merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
message = "#{user_name} #{state} #{merge_request_link} in " \
"#{project_link}: <b>#{title}</b>"
@@ -224,7 +224,7 @@ class HipchatService < Service
when "MergeRequest"
subj_attr = HashWithIndifferentAccess.new(data[:merge_request])
subject_id = subj_attr[:iid]
- subject_desc = "##{subject_id}"
+ subject_desc = "!#{subject_id}"
subject_type = "merge request"
title = format_title(subj_attr[:title])
when "Snippet"
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index d89cf6d17b2..fd65027f084 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -60,7 +60,7 @@ class SlackService < Service
end
def supported_events
- %w(push issue merge_request note tag_push build)
+ %w(push issue merge_request note tag_push build wiki_page)
end
def execute(data)
@@ -90,6 +90,8 @@ class SlackService < Service
NoteMessage.new(data)
when "build"
BuildMessage.new(data) if should_build_be_notified?(data)
+ when "wiki_page"
+ WikiPageMessage.new(data)
end
opt = {}
@@ -133,3 +135,4 @@ require "slack_service/push_message"
require "slack_service/merge_message"
require "slack_service/note_message"
require "slack_service/build_message"
+require "slack_service/wiki_page_message"
diff --git a/app/models/project_services/slack_service/merge_message.rb b/app/models/project_services/slack_service/merge_message.rb
index e792c258f73..11fc691022b 100644
--- a/app/models/project_services/slack_service/merge_message.rb
+++ b/app/models/project_services/slack_service/merge_message.rb
@@ -50,7 +50,7 @@ class SlackService
end
def merge_request_link
- "[merge request ##{merge_request_id}](#{merge_request_url})"
+ "[merge request !#{merge_request_id}](#{merge_request_url})"
end
def merge_request_url
diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb
index b15d9a14677..89ba51cb662 100644
--- a/app/models/project_services/slack_service/note_message.rb
+++ b/app/models/project_services/slack_service/note_message.rb
@@ -58,7 +58,7 @@ class SlackService
def create_merge_note(merge_request)
commented_on_message(
- "[merge request ##{merge_request[:iid]}](#{@note_url})",
+ "[merge request !#{merge_request[:iid]}](#{@note_url})",
format_title(merge_request[:title]))
end
diff --git a/app/models/project_services/slack_service/wiki_page_message.rb b/app/models/project_services/slack_service/wiki_page_message.rb
new file mode 100644
index 00000000000..f336d9e7691
--- /dev/null
+++ b/app/models/project_services/slack_service/wiki_page_message.rb
@@ -0,0 +1,53 @@
+class SlackService
+ class WikiPageMessage < BaseMessage
+ attr_reader :user_name
+ attr_reader :title
+ attr_reader :project_name
+ attr_reader :project_url
+ attr_reader :wiki_page_url
+ attr_reader :action
+ attr_reader :description
+
+ def initialize(params)
+ @user_name = params[:user][:name]
+ @project_name = params[:project_name]
+ @project_url = params[:project_url]
+
+ obj_attr = params[:object_attributes]
+ obj_attr = HashWithIndifferentAccess.new(obj_attr)
+ @title = obj_attr[:title]
+ @wiki_page_url = obj_attr[:url]
+ @description = obj_attr[:content]
+
+ @action =
+ case obj_attr[:action]
+ when "create"
+ "created"
+ when "update"
+ "edited"
+ end
+ end
+
+ def attachments
+ description_message
+ end
+
+ private
+
+ def message
+ "#{user_name} #{action} #{wiki_page_link} in #{project_link}: *#{title}*"
+ end
+
+ def description_message
+ [{ text: format(@description), color: attachment_color }]
+ end
+
+ def project_link
+ "[#{project_name}](#{project_url})"
+ end
+
+ def wiki_page_link
+ "[wiki page](#{wiki_page_url})"
+ end
+ end
+end
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index b8e9416131a..8dceee5e2c5 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -85,13 +85,15 @@ class TeamcityService < CiService
end
def build_info(sha)
- url = URI.parse("#{teamcity_url}/httpAuth/app/rest/builds/"\
- "branch:unspecified:any,number:#{sha}")
+ url = URI.join(
+ teamcity_url,
+ "/httpAuth/app/rest/builds/branch:unspecified:any,number:#{sha}"
+ ).to_s
auth = {
username: username,
- password: password,
+ password: password
}
- @response = HTTParty.get("#{url}", verify: false, basic_auth: auth)
+ @response = HTTParty.get(url, verify: false, basic_auth: auth)
end
def build_page(sha, ref)
@@ -100,12 +102,14 @@ class TeamcityService < CiService
if @response.code != 200
# If actual build link can't be determined,
# send user to build summary page.
- "#{teamcity_url}/viewLog.html?buildTypeId=#{build_type}"
+ URI.join(teamcity_url, "/viewLog.html?buildTypeId=#{build_type}").to_s
else
# If actual build link is available, go to build result page.
built_id = @response['build']['id']
- "#{teamcity_url}/viewLog.html?buildId=#{built_id}"\
- "&buildTypeId=#{build_type}"
+ URI.join(
+ teamcity_url,
+ "/viewLog.html?buildId=#{built_id}&buildTypeId=#{build_type}"
+ ).to_s
end
end
@@ -140,12 +144,13 @@ class TeamcityService < CiService
branch = Gitlab::Git.ref_name(data[:ref])
- self.class.post("#{teamcity_url}/httpAuth/app/rest/buildQueue",
- body: "<build branchName=\"#{branch}\">"\
- "<buildType id=\"#{build_type}\"/>"\
- '</build>',
- headers: { 'Content-type' => 'application/xml' },
- basic_auth: auth
- )
+ self.class.post(
+ URI.join(teamcity_url, '/httpAuth/app/rest/buildQueue').to_s,
+ body: "<build branchName=\"#{branch}\">"\
+ "<buildType id=\"#{build_type}\"/>"\
+ '</build>',
+ headers: { 'Content-type' => 'application/xml' },
+ basic_auth: auth
+ )
end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 0b2289cfa39..61c8dce6060 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -12,11 +12,13 @@ class Repository
attr_accessor :path_with_namespace, :project
def self.clean_old_archives
- repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
+ Gitlab::Metrics.measure(:clean_old_archives) do
+ repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
- return unless File.directory?(repository_downloads_path)
+ return unless File.directory?(repository_downloads_path)
- Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
+ Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
+ end
end
def initialize(path_with_namespace, project)
@@ -169,7 +171,12 @@ class Repository
def rm_tag(tag_name)
before_remove_tag
- gitlab_shell.rm_tag(path_with_namespace, tag_name)
+ begin
+ rugged.tags.delete(tag_name)
+ true
+ rescue Rugged::ReferenceError
+ false
+ end
end
def branch_names
@@ -221,7 +228,8 @@ class Repository
def cache_keys
%i(size branch_names tag_names commit_count
- readme version contribution_guide changelog license)
+ readme version contribution_guide changelog
+ license_blob license_key)
end
def build_cache
@@ -454,27 +462,21 @@ class Repository
end
end
- def license
- cache.fetch(:license) do
- licenses = tree(:head).blobs.find_all do |file|
- file.name =~ /\A(copying|license|licence)/i
- end
-
- preferences = [
- /\Alicen[sc]e\z/i, # LICENSE, LICENCE
- /\Alicen[sc]e\./i, # LICENSE.md, LICENSE.txt
- /\Acopying\z/i, # COPYING
- /\Acopying\.(?!lesser)/i, # COPYING.txt
- /Acopying.lesser/i # COPYING.LESSER
- ]
+ def license_blob
+ return nil if !exists? || empty?
- license = nil
- preferences.each do |r|
- license = licenses.find { |l| l.name =~ r }
- break if license
+ cache.fetch(:license_blob) do
+ tree(:head).blobs.find do |file|
+ file.name =~ /\A(licen[sc]e|copying)(\..+|\z)/i
end
+ end
+ end
- license
+ def license_key
+ return nil if !exists? || empty?
+
+ cache.fetch(:license_key) do
+ Licensee.license(path).try(:key)
end
end
@@ -542,15 +544,18 @@ class Repository
commit(sha)
end
- def next_patch_branch
- patch_branch_ids = self.branch_names.map do |n|
- result = n.match(/\Apatch-([0-9]+)\z/)
+ def next_branch(name, opts={})
+ branch_ids = self.branch_names.map do |n|
+ next 1 if n == name
+ result = n.match(/\A#{name}-([0-9]+)\z/)
result[1].to_i if result
end.compact
- highest_patch_branch_id = patch_branch_ids.max || 0
+ highest_branch_id = branch_ids.max || 0
- "patch-#{highest_patch_branch_id + 1}"
+ return name if opts[:mild] && 0 == highest_branch_id
+
+ "#{name}-#{highest_branch_id + 1}"
end
# Remove archives older than 2 hours
@@ -753,6 +758,28 @@ class Repository
end
end
+ def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil)
+ source_sha = find_branch(base_branch).target
+ cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch)
+
+ return false unless cherry_pick_tree_id
+
+ commit_with_hooks(user, base_branch) do |ref|
+ committer = user_to_committer(user)
+ source_sha = Rugged::Commit.create(rugged,
+ message: commit.message,
+ author: {
+ email: commit.author_email,
+ name: commit.author_name,
+ time: commit.authored_date
+ },
+ committer: committer,
+ tree: cherry_pick_tree_id,
+ parents: [rugged.lookup(source_sha)],
+ update_ref: ref)
+ end
+ end
+
def check_revert_content(commit, base_branch)
source_sha = find_branch(base_branch).target
args = [commit.id, source_sha]
@@ -767,6 +794,20 @@ class Repository
tree_id
end
+ def check_cherry_pick_content(commit, base_branch)
+ source_sha = find_branch(base_branch).target
+ args = [commit.id, source_sha]
+ args << 1 if commit.merge_commit?
+
+ cherry_pick_index = rugged.cherrypick_commit(*args)
+ return false if cherry_pick_index.conflicts?
+
+ tree_id = cherry_pick_index.write_tree(rugged)
+ return false unless diff_exists?(source_sha, tree_id)
+
+ tree_id
+ end
+
def diff_exists?(sha1, sha2)
rugged.diff(sha1, sha2).size > 0
end
@@ -797,7 +838,7 @@ class Repository
def search_files(query, ref)
offset = 2
- args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref})
+ args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{Regexp.escape(query)} #{ref || root_ref})
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 721273250ea..2645b8321d7 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -32,6 +32,7 @@ class Service < ActiveRecord::Base
default_value_for :tag_push_events, true
default_value_for :note_events, true
default_value_for :build_events, true
+ default_value_for :wiki_page_events, true
after_initialize :initialize_properties
@@ -53,6 +54,7 @@ class Service < ActiveRecord::Base
scope :merge_request_hooks, -> { where(merge_requests_events: true, active: true) }
scope :note_hooks, -> { where(note_events: true, active: true) }
scope :build_hooks, -> { where(build_events: true, active: true) }
+ scope :wiki_page_hooks, -> { where(wiki_page_events: true, active: true) }
default_value_for :category, 'common'
@@ -94,7 +96,7 @@ class Service < ActiveRecord::Base
end
def supported_events
- %w(push tag_push issue merge_request)
+ %w(push tag_push issue merge_request wiki_page)
end
def execute(data)
diff --git a/app/models/user.rb b/app/models/user.rb
index 031315debd7..ab48f8f1960 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -63,7 +63,6 @@
#
require 'carrierwave/orm/activerecord'
-require 'file_size_validator'
class User < ActiveRecord::Base
extend Gitlab::ConfigHelper
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index 526760779a4..3d5fd9d3ee9 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -29,6 +29,10 @@ class WikiPage
# new Page values before writing to the Gollum repository.
attr_accessor :attributes
+ def hook_attrs
+ attributes
+ end
+
def initialize(wiki, page = nil, persisted = false)
@wiki = wiki
@page = page