diff options
author | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-04-13 16:38:24 +0200 |
---|---|---|
committer | Kamil Trzcinski <ayufan@ayufan.eu> | 2016-04-13 16:38:24 +0200 |
commit | ed3f267497a1348f7e50e396e1d3569f2da8d48b (patch) | |
tree | 4f2ce69ffec0447ed3bea34e73241ddfa24d2f40 /app/models | |
parent | f5d24e60f842096f670593fb4dd0d29c3f5d4fcc (diff) | |
parent | 9e68109f2d454dd05cf42a03a41a2e858e1e11bc (diff) | |
download | gitlab-ce-ed3f267497a1348f7e50e396e1d3569f2da8d48b.tar.gz |
Merge branch 'ci-commit-as-pipeline' into with-pipeline-view
* ci-commit-as-pipeline: (131 commits)
Optimise Merge Request builds rendering
Fix migrations on MySQL
Update db/schema.rb
Cleanup changes
Cleanup required migrations
Add indexes concurrently on PostgreSQL
Fix CiStatus implementation and tests
Fix group_member_spec to not leak information
Fix doc for moving an issue
Update tests for moving issues via API
Tie example config to JIRA screenshot
API: Ability to move an issue
Added ability to add custom tags to transactions
API: Avoid group leak while updating the group
API: Return 404 if user does not have access to group
Move 'New branch from issue' feature doc to web_editor.md
Fix repository cache invalidation issue when project is recreated with an empty repo
improve formatting
Add `Gitlab.com?` method
(doc) fix typo to ssh keys doc url
...
Conflicts:
app/controllers/projects/pipelines_controller.rb
app/helpers/ci_status_helper.rb
app/helpers/gitlab_routing_helper.rb
app/views/projects/ci/commits/_commit.html.haml
app/views/projects/commit/_ci_commit.html.haml
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/commit.rb | 8 | ||||
-rw-r--r-- | app/models/commit_status.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/ci_status.rb | 20 | ||||
-rw-r--r-- | app/models/concerns/notifiable.rb | 15 | ||||
-rw-r--r-- | app/models/group.rb | 1 | ||||
-rw-r--r-- | app/models/member.rb | 12 | ||||
-rw-r--r-- | app/models/members/group_member.rb | 1 | ||||
-rw-r--r-- | app/models/members/project_member.rb | 1 | ||||
-rw-r--r-- | app/models/merge_request.rb | 6 | ||||
-rw-r--r-- | app/models/notification.rb | 77 | ||||
-rw-r--r-- | app/models/notification_setting.rb | 28 | ||||
-rw-r--r-- | app/models/project.rb | 11 | ||||
-rw-r--r-- | app/models/repository.rb | 8 | ||||
-rw-r--r-- | app/models/user.rb | 18 |
14 files changed, 86 insertions, 122 deletions
diff --git a/app/models/commit.rb b/app/models/commit.rb index a898f7ba337..9ffdcc59128 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -150,13 +150,11 @@ class Commit end def hook_attrs(with_changed_files: false) - path_with_namespace = project.path_with_namespace - data = { id: id, message: safe_message, timestamp: committed_date.xmlschema, - url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{id}", + url: commit_url, author: { name: author_name, email: author_email @@ -170,6 +168,10 @@ 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) diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 882595ca21e..66eb5dcecf9 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -51,8 +51,6 @@ class CommitStatus < ActiveRecord::Base scope :ordered, -> { order(:ref, :stage_idx, :name) } scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) } - AVAILABLE_STATUSES = ['pending', 'running', 'success', 'failed', 'canceled'] - state_machine :status, initial: :pending do event :run do transition pending: :running diff --git a/app/models/concerns/ci_status.rb b/app/models/concerns/ci_status.rb index 4c7089bc36c..67e15b2d55b 100644 --- a/app/models/concerns/ci_status.rb +++ b/app/models/concerns/ci_status.rb @@ -1,15 +1,17 @@ module CiStatus extend ActiveSupport::Concern - module ClassMethods + AVAILABLE_STATUSES = %w(pending running success failed canceled skipped) + + class_methods do def status_sql - builds = all.select('count(id)').to_sql - success = all.success.select('count(id)').to_sql - ignored = all.failed.where(allow_failure: true).select('count(id)').to_sql if all.try(:ignored) + 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 ||= '0' - pending = all.pending.select('count(id)').to_sql - running = all.running.select('count(id)').to_sql - canceled = all.canceled.select('count(id)').to_sql + pending = all.pending.select('count(*)').to_sql + running = all.running.select('count(*)').to_sql + canceled = all.canceled.select('count(*)').to_sql deduce_status = "(CASE WHEN (#{builds})=0 THEN 'skipped' @@ -24,7 +26,7 @@ module CiStatus end def status - pluck(self.status_sql).first + all.pluck(self.status_sql).first end def duration @@ -34,7 +36,7 @@ module CiStatus end included do - validates :status, inclusion: { in: %w(pending running failed success canceled skipped) } + validates :status, inclusion: { in: AVAILABLE_STATUSES } state_machine :status, initial: :pending do state :pending, value: 'pending' diff --git a/app/models/concerns/notifiable.rb b/app/models/concerns/notifiable.rb deleted file mode 100644 index d7dcd97911d..00000000000 --- a/app/models/concerns/notifiable.rb +++ /dev/null @@ -1,15 +0,0 @@ -# == Notifiable concern -# -# Contains notification functionality -# -module Notifiable - extend ActiveSupport::Concern - - included do - validates :notification_level, inclusion: { in: Notification.project_notification_levels }, presence: true - end - - def notification - @notification ||= Notification.new(self) - end -end diff --git a/app/models/group.rb b/app/models/group.rb index b332601c59b..9a04ac70d35 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -27,6 +27,7 @@ class Group < Namespace has_many :users, through: :group_members has_many :project_group_links, dependent: :destroy has_many :shared_projects, through: :project_group_links, source: :project + has_many :notification_settings, dependent: :destroy, as: :source validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :visibility_level_allowed_by_projects diff --git a/app/models/member.rb b/app/models/member.rb index ca08007b7eb..60efafef211 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -19,7 +19,6 @@ class Member < ActiveRecord::Base include Sortable - include Notifiable include Gitlab::Access attr_accessor :raw_invite_token @@ -56,12 +55,15 @@ class Member < ActiveRecord::Base before_validation :generate_invite_token, on: :create, if: -> (member) { member.invite_email.present? } after_create :send_invite, if: :invite? + after_create :create_notification_setting, unless: :invite? after_create :post_create_hook, unless: :invite? after_update :post_update_hook, unless: :invite? after_destroy :post_destroy_hook, unless: :invite? delegate :name, :username, :email, to: :user, prefix: true + default_value_for :notification_level, NotificationSetting.levels[:global] + class << self def find_by_invite_token(invite_token) invite_token = Devise.token_generator.digest(self, :invite_token, invite_token) @@ -160,6 +162,14 @@ class Member < ActiveRecord::Base send_invite end + def create_notification_setting + user.notification_settings.find_or_create_for(source) + end + + def notification_setting + @notification_setting ||= user.notification_settings_for(source) + end + private def send_invite diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb index 65d2ea00570..9fb474a1a93 100644 --- a/app/models/members/group_member.rb +++ b/app/models/members/group_member.rb @@ -24,7 +24,6 @@ class GroupMember < Member # Make sure group member points only to group as it source default_value_for :source_type, SOURCE_TYPE - default_value_for :notification_level, Notification::N_GLOBAL validates_format_of :source_type, with: /\ANamespace\z/ default_scope { where(source_type: SOURCE_TYPE) } diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index 560d1690e14..07ddb02ae9d 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -27,7 +27,6 @@ class ProjectMember < Member # Make sure project member points only to project as it source default_value_for :source_type, SOURCE_TYPE - default_value_for :notification_level, Notification::N_GLOBAL validates_format_of :source_type, with: /\AProject\z/ default_scope { where(source_type: SOURCE_TYPE) } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 33869e215c9..6766e4d1afa 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -128,7 +128,7 @@ class MergeRequest < ActiveRecord::Base validates :target_project, presence: true validates :target_branch, presence: true validates :merge_user, presence: true, if: :merge_when_build_succeeds? - validate :validate_branches + validate :validate_branches, unless: :allow_broken validate :validate_fork scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } @@ -218,7 +218,7 @@ class MergeRequest < ActiveRecord::Base end if opened? || reopened? - similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.id).opened + similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.try(:id)).opened similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id if similar_mrs.any? errors.add :validate_branches, @@ -345,7 +345,7 @@ class MergeRequest < ActiveRecord::Base def hook_attrs attrs = { - source: source_project.hook_attrs, + source: source_project.try(:hook_attrs), target: target_project.hook_attrs, last_commit: nil, work_in_progress: work_in_progress? diff --git a/app/models/notification.rb b/app/models/notification.rb deleted file mode 100644 index 171b8df45c2..00000000000 --- a/app/models/notification.rb +++ /dev/null @@ -1,77 +0,0 @@ -class Notification - # - # Notification levels - # - N_DISABLED = 0 - N_PARTICIPATING = 1 - N_WATCH = 2 - N_GLOBAL = 3 - N_MENTION = 4 - - attr_accessor :target - - class << self - def notification_levels - [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH] - end - - def options_with_labels - { - disabled: N_DISABLED, - participating: N_PARTICIPATING, - watch: N_WATCH, - mention: N_MENTION, - global: N_GLOBAL - } - end - - def project_notification_levels - [N_DISABLED, N_MENTION, N_PARTICIPATING, N_WATCH, N_GLOBAL] - end - end - - def initialize(target) - @target = target - end - - def disabled? - target.notification_level == N_DISABLED - end - - def participating? - target.notification_level == N_PARTICIPATING - end - - def watch? - target.notification_level == N_WATCH - end - - def global? - target.notification_level == N_GLOBAL - end - - def mention? - target.notification_level == N_MENTION - end - - def level - target.notification_level - end - - def to_s - case level - when N_DISABLED - 'Disabled' - when N_PARTICIPATING - 'Participating' - when N_WATCH - 'Watching' - when N_MENTION - 'On mention' - when N_GLOBAL - 'Global' - else - # do nothing - end - end -end diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb new file mode 100644 index 00000000000..5001738f411 --- /dev/null +++ b/app/models/notification_setting.rb @@ -0,0 +1,28 @@ +class NotificationSetting < ActiveRecord::Base + enum level: { disabled: 0, participating: 1, watch: 2, global: 3, mention: 4 } + + default_value_for :level, NotificationSetting.levels[:global] + + belongs_to :user + belongs_to :source, polymorphic: true + + validates :user, presence: true + validates :source, presence: true + validates :level, presence: true + validates :user_id, uniqueness: { scope: [:source_type, :source_id], + message: "already exists in source", + allow_nil: true } + + scope :for_groups, -> { where(source_type: 'Namespace') } + scope :for_projects, -> { where(source_type: 'Project') } + + def self.find_or_create_for(source) + setting = find_or_initialize_by(source: source) + + unless setting.persisted? + setting.save + end + + setting + end +end diff --git a/app/models/project.rb b/app/models/project.rb index b9d589a4594..e48830115bc 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -154,6 +154,7 @@ class Project < ActiveRecord::Base has_many :project_group_links, dependent: :destroy has_many :invited_groups, through: :project_group_links, source: :group has_many :todos, dependent: :destroy + has_many :notification_settings, dependent: :destroy, as: :source has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" @@ -388,9 +389,15 @@ class Project < ActiveRecord::Base def add_import_job if forked? - RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path) + job_id = RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path) else - RepositoryImportWorker.perform_async(self.id) + job_id = RepositoryImportWorker.perform_async(self.id) + end + + if job_id + Rails.logger.info "Import job started for #{path_with_namespace} with job ID #{job_id}" + else + Rails.logger.error "Import job failed to start for #{path_with_namespace}" end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 8dead3a5884..0b2289cfa39 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -253,6 +253,8 @@ class Repository # This ensures this particular cache is flushed after the first commit to a # new repository. expire_emptiness_caches if empty? + expire_branch_count_cache + expire_tag_count_cache end def expire_branch_cache(branch_name = nil) @@ -896,9 +898,9 @@ class Repository end def main_language - unless empty? - Linguist::Repository.new(rugged, rugged.head.target_id).language - end + return if empty? || rugged.head_unborn? + + Linguist::Repository.new(rugged, rugged.head.target_id).language end def avatar diff --git a/app/models/user.rb b/app/models/user.rb index 2b0bee2099f..031315debd7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -143,6 +143,7 @@ class User < ActiveRecord::Base has_many :spam_logs, dependent: :destroy has_many :builds, dependent: :nullify, class_name: 'Ci::Build' has_many :todos, dependent: :destroy + has_many :notification_settings, dependent: :destroy # # Validations @@ -157,7 +158,7 @@ class User < ActiveRecord::Base presence: true, uniqueness: { case_sensitive: false } - validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true + validates :notification_level, presence: true validate :namespace_uniq, if: ->(user) { user.username_changed? } validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validate :unique_email, if: ->(user) { user.email_changed? } @@ -190,6 +191,13 @@ class User < ActiveRecord::Base # Note: When adding an option, it MUST go on the end of the array. enum project_view: [:readme, :activity, :files] + # Notification level + # Note: When adding an option, it MUST go on the end of the array. + # + # TODO: Add '_prefix: :notification' to enum when update to Rails 5. https://github.com/rails/rails/pull/19813 + # Because user.notification_disabled? is much better than user.disabled? + enum notification_level: [:disabled, :participating, :watch, :global, :mention] + alias_attribute :private_token, :authentication_token delegate :path, to: :namespace, allow_nil: true, prefix: true @@ -349,10 +357,6 @@ class User < ActiveRecord::Base "#{self.class.reference_prefix}#{username}" end - def notification - @notification ||= Notification.new(self) - end - def generate_password if self.force_random_password self.password = self.password_confirmation = Devise.friendly_token.first(8) @@ -827,6 +831,10 @@ class User < ActiveRecord::Base end end + def notification_settings_for(source) + notification_settings.find_or_initialize_by(source: source) + end + private def projects_union |