diff options
Diffstat (limited to 'app/models')
-rw-r--r-- | app/models/appearance.rb | 1 | ||||
-rw-r--r-- | app/models/application_setting.rb | 30 | ||||
-rw-r--r-- | app/models/chat_team.rb | 5 | ||||
-rw-r--r-- | app/models/ci/build.rb | 23 | ||||
-rw-r--r-- | app/models/ci/pipeline.rb | 5 | ||||
-rw-r--r-- | app/models/ci/runner.rb | 13 | ||||
-rw-r--r-- | app/models/ci/trigger.rb | 11 | ||||
-rw-r--r-- | app/models/commit_status.rb | 14 | ||||
-rw-r--r-- | app/models/concerns/awardable.rb | 2 | ||||
-rw-r--r-- | app/models/concerns/has_status.rb | 26 | ||||
-rw-r--r-- | app/models/external_issue.rb | 5 | ||||
-rw-r--r-- | app/models/group.rb | 13 | ||||
-rw-r--r-- | app/models/merge_request.rb | 5 | ||||
-rw-r--r-- | app/models/namespace.rb | 1 | ||||
-rw-r--r-- | app/models/note.rb | 13 | ||||
-rw-r--r-- | app/models/project.rb | 6 | ||||
-rw-r--r-- | app/models/project_services/kubernetes_service.rb | 7 | ||||
-rw-r--r-- | app/models/repository.rb | 35 | ||||
-rw-r--r-- | app/models/snippet.rb | 2 | ||||
-rw-r--r-- | app/models/upload.rb | 63 | ||||
-rw-r--r-- | app/models/user.rb | 3 |
21 files changed, 226 insertions, 57 deletions
diff --git a/app/models/appearance.rb b/app/models/appearance.rb index e4106e1c2e9..c79326e8427 100644 --- a/app/models/appearance.rb +++ b/app/models/appearance.rb @@ -10,4 +10,5 @@ class Appearance < ActiveRecord::Base mount_uploader :logo, AttachmentUploader mount_uploader :header_logo, AttachmentUploader + has_many :uploads, as: :model, dependent: :destroy end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index dc36c754438..be632930895 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -64,6 +64,16 @@ class ApplicationSetting < ActiveRecord::Base presence: true, if: :akismet_enabled + validates :unique_ips_limit_per_user, + numericality: { greater_than_or_equal_to: 1 }, + presence: true, + if: :unique_ips_limit_enabled + + validates :unique_ips_limit_time_window, + numericality: { greater_than_or_equal_to: 0 }, + presence: true, + if: :unique_ips_limit_enabled + validates :koding_url, presence: true, if: :koding_enabled @@ -179,10 +189,14 @@ class ApplicationSetting < ActiveRecord::Base default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_projects_limit: Settings.gitlab['default_projects_limit'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], + default_group_visibility: Settings.gitlab.default_projects_features['visibility_level'], disabled_oauth_sign_in_sources: [], domain_whitelist: Settings.gitlab['domain_whitelist'], gravatar_enabled: Settings.gravatar['enabled'], help_page_text: nil, + unique_ips_limit_per_user: 10, + unique_ips_limit_time_window: 3600, + unique_ips_limit_enabled: false, housekeeping_bitmaps_enabled: true, housekeeping_enabled: true, housekeeping_full_repack_period: 50, @@ -277,6 +291,22 @@ class ApplicationSetting < ActiveRecord::Base self.repository_storages = [value] end + def default_project_visibility=(level) + super(Gitlab::VisibilityLevel.level_value(level)) + end + + def default_snippet_visibility=(level) + super(Gitlab::VisibilityLevel.level_value(level)) + end + + def default_group_visibility=(level) + super(Gitlab::VisibilityLevel.level_value(level)) + end + + def restricted_visibility_levels=(levels) + super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) }) + end + # Choose one of the available repository storage options. Currently all have # equal weighting. def pick_repository_storage diff --git a/app/models/chat_team.rb b/app/models/chat_team.rb new file mode 100644 index 00000000000..7952141a0d6 --- /dev/null +++ b/app/models/chat_team.rb @@ -0,0 +1,5 @@ +class ChatTeam < ActiveRecord::Base + validates :team_id, presence: true + + belongs_to :namespace +end diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index f2989eff22d..d69643967a1 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -63,6 +63,10 @@ module Ci end state_machine :status do + event :actionize do + transition created: :manual + end + after_transition any => [:pending] do |build| build.run_after_commit do BuildQueueWorker.perform_async(id) @@ -94,16 +98,21 @@ module Ci .fabricate! end - def manual? - self.when == 'manual' - end - def other_actions pipeline.manual_actions.where.not(name: name) end def playable? - project.builds_enabled? && commands.present? && manual? && skipped? + project.builds_enabled? && has_commands? && + action? && manual? + end + + def action? + self.when == 'manual' + end + + def has_commands? + commands.present? end def play(current_user) @@ -122,7 +131,7 @@ module Ci end def retryable? - project.builds_enabled? && commands.present? && + project.builds_enabled? && has_commands? && (success? || failed? || canceled?) end @@ -552,7 +561,7 @@ module Ci ] variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag? variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request - variables << { key: 'CI_BUILD_MANUAL', value: 'true', public: true } if manual? + variables << { key: 'CI_BUILD_MANUAL', value: 'true', public: true } if action? variables end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 80e11a5b58f..67206415f7b 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -49,6 +49,10 @@ module Ci transition any - [:canceled] => :canceled end + event :block do + transition any - [:manual] => :manual + end + # IMPORTANT # Do not add any operations to this state_machine # Create a separate worker for each new operation @@ -321,6 +325,7 @@ module Ci when 'failed' then drop when 'canceled' then cancel when 'skipped' then skip + when 'manual' then block end end end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 4863c34a6a6..edd21f984c8 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -127,18 +127,15 @@ module Ci def tick_runner_queue SecureRandom.hex.tap do |new_update| - Gitlab::Redis.with do |redis| - redis.set(runner_queue_key, new_update, ex: RUNNER_QUEUE_EXPIRY_TIME) - end + ::Gitlab::Workhorse.set_key_and_notify(runner_queue_key, new_update, + expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: true) end end def ensure_runner_queue_value - Gitlab::Redis.with do |redis| - value = SecureRandom.hex - redis.set(runner_queue_key, value, ex: RUNNER_QUEUE_EXPIRY_TIME, nx: true) - redis.get(runner_queue_key) - end + new_value = SecureRandom.hex + ::Gitlab::Workhorse.set_key_and_notify(runner_queue_key, new_value, + expire: RUNNER_QUEUE_EXPIRY_TIME, overwrite: false) end def is_runner_queue_value_latest?(value) diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb index 39a1dd86241..8aa45b2f02e 100644 --- a/app/models/ci/trigger.rb +++ b/app/models/ci/trigger.rb @@ -5,10 +5,11 @@ module Ci acts_as_paranoid belongs_to :project, foreign_key: :gl_project_id + belongs_to :owner, class_name: "User" + has_many :trigger_requests, dependent: :destroy - validates :token, presence: true - validates :token, uniqueness: true + validates :token, presence: true, uniqueness: true before_validation :set_default_values @@ -25,7 +26,11 @@ module Ci end def short_token - token[0...10] + token[0...4] + end + + def can_show_token?(user) + owner.blank? || owner == user end end end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index fc750a3e5e9..7e23e14794f 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -29,9 +29,11 @@ class CommitStatus < ActiveRecord::Base end scope :exclude_ignored, -> do - # We want to ignore failed_but_allowed jobs + # We want to ignore failed but allowed to fail jobs. + # + # TODO, we also skip ignored optional manual actions. where("allow_failure = ? OR status IN (?)", - false, all_state_names - [:failed, :canceled]) + false, all_state_names - [:failed, :canceled, :manual]) end scope :retried, -> { where.not(id: latest) } @@ -42,11 +44,11 @@ class CommitStatus < ActiveRecord::Base state_machine :status do event :enqueue do - transition [:created, :skipped] => :pending + transition [:created, :skipped, :manual] => :pending end event :process do - transition skipped: :created + transition [:skipped, :manual] => :created end event :run do @@ -66,7 +68,7 @@ class CommitStatus < ActiveRecord::Base end event :cancel do - transition [:created, :pending, :running] => :canceled + transition [:created, :pending, :running, :manual] => :canceled end before_transition created: [:pending, :running] do |commit_status| @@ -86,7 +88,7 @@ class CommitStatus < ActiveRecord::Base commit_status.run_after_commit do pipeline.try do |pipeline| - if complete? + if complete? || manual? PipelineProcessWorker.perform_async(pipeline.id) else PipelineUpdateWorker.perform_async(pipeline.id) diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb index 073ac4c1b65..a7fd0a15f0f 100644 --- a/app/models/concerns/awardable.rb +++ b/app/models/concerns/awardable.rb @@ -101,6 +101,6 @@ module Awardable private def normalize_name(name) - Gitlab::AwardEmoji.normalize_emoji_name(name) + Gitlab::Emoji.normalize_emoji_name(name) end end diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index aea359e70bb..b819947c9e6 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -2,22 +2,21 @@ module HasStatus extend ActiveSupport::Concern DEFAULT_STATUS = 'created'.freeze - AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped].freeze - STARTED_STATUSES = %w[running success failed skipped].freeze + BLOCKED_STATUS = 'manual'.freeze + AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped manual].freeze + STARTED_STATUSES = %w[running success failed skipped manual].freeze ACTIVE_STATUSES = %w[pending running].freeze COMPLETED_STATUSES = %w[success failed canceled skipped].freeze - ORDERED_STATUSES = %w[failed pending running canceled success skipped].freeze + ORDERED_STATUSES = %w[manual failed pending running canceled success skipped].freeze class_methods do def status_sql - scope = if respond_to?(:exclude_ignored) - exclude_ignored - else - all - end + scope = respond_to?(:exclude_ignored) ? exclude_ignored : all + builds = scope.select('count(*)').to_sql created = scope.created.select('count(*)').to_sql success = scope.success.select('count(*)').to_sql + manual = scope.manual.select('count(*)').to_sql pending = scope.pending.select('count(*)').to_sql running = scope.running.select('count(*)').to_sql skipped = scope.skipped.select('count(*)').to_sql @@ -30,7 +29,8 @@ module HasStatus WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success' WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled' WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending' - WHEN (#{running})+(#{pending})+(#{created})>0 THEN 'running' + WHEN (#{running})+(#{pending})>0 THEN 'running' + WHEN (#{manual})>0 THEN 'manual' ELSE 'failed' END)" end @@ -63,6 +63,7 @@ module HasStatus state :success, value: 'success' state :canceled, value: 'canceled' state :skipped, value: 'skipped' + state :manual, value: 'manual' end scope :created, -> { where(status: 'created') } @@ -73,12 +74,13 @@ module HasStatus scope :failed, -> { where(status: 'failed') } scope :canceled, -> { where(status: 'canceled') } scope :skipped, -> { where(status: 'skipped') } + scope :manual, -> { where(status: 'manual') } scope :running_or_pending, -> { where(status: [:running, :pending]) } scope :finished, -> { where(status: [:success, :failed, :canceled]) } scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) } scope :cancelable, -> do - where(status: [:running, :pending, :created]) + where(status: [:running, :pending, :created, :manual]) end end @@ -94,6 +96,10 @@ module HasStatus COMPLETED_STATUSES.include?(status) end + def blocked? + BLOCKED_STATUS == status + end + private def calculate_duration diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb index b973bbcd8da..e63f89a9f85 100644 --- a/app/models/external_issue.rb +++ b/app/models/external_issue.rb @@ -24,6 +24,11 @@ class ExternalIssue def ==(other) other.is_a?(self.class) && (to_s == other.to_s) end + alias_method :eql?, :== + + def hash + [self.class, to_s].hash + end def project @project diff --git a/app/models/group.rb b/app/models/group.rb index 240a17f1dc1..bd0ecae3da4 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -28,6 +28,7 @@ class Group < Namespace validates :avatar, file_size: { maximum: 200.kilobytes.to_i } mount_uploader :avatar, AvatarUploader + has_many :uploads, as: :model, dependent: :destroy after_create :post_create_hook after_destroy :post_destroy_hook @@ -93,7 +94,7 @@ class Group < Namespace end def visibility_level_field - visibility_level + :visibility_level end def visibility_level_allowed_by_projects @@ -212,4 +213,14 @@ class Group < Namespace def users_with_parents User.where(id: members_with_parents.select(:user_id)) end + + def mattermost_team_params + max_length = 59 + + { + name: path[0..max_length], + display_name: name[0..max_length], + type: public? ? 'O' : 'I' # Open vs Invite-only + } + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 81bde54d5dc..0f7b8311588 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -684,7 +684,10 @@ class MergeRequest < ActiveRecord::Base end def has_ci? - source_project.try(:ci_service) && commits.any? + has_ci_integration = source_project.try(:ci_service) + uses_gitlab_ci = all_pipelines.any? + + (has_ci_integration || uses_gitlab_ci) && commits.any? end def branch_missing? diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 3137dd32f93..d350f1d6770 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -20,6 +20,7 @@ class Namespace < ActiveRecord::Base belongs_to :parent, class_name: "Namespace" has_many :children, class_name: "Namespace", foreign_key: :parent_id + has_one :chat_team, dependent: :destroy validates :owner, presence: true, unless: ->(n) { n.type == "Group" } validates :name, diff --git a/app/models/note.rb b/app/models/note.rb index 4c97e4a986c..e22e96aec6f 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -85,6 +85,7 @@ class Note < ActiveRecord::Base before_validation :nullify_blank_type, :nullify_blank_line_code before_validation :set_discussion_id after_save :keep_around_commit, unless: :for_personal_snippet? + after_save :expire_etag_cache class << self def model_name @@ -272,4 +273,16 @@ class Note < ActiveRecord::Base self.class.build_discussion_id(noteable_type, noteable_id || commit_id) end end + + def expire_etag_cache + return unless for_issue? + + key = Gitlab::Routing.url_helpers.namespace_project_noteable_notes_path( + noteable.project.namespace, + noteable.project, + target_type: noteable_type.underscore, + target_id: noteable.id + ) + Gitlab::EtagCaching::Store.new.touch(key) + end end diff --git a/app/models/project.rb b/app/models/project.rb index 0c2494d3c32..7d211784c3c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -113,6 +113,7 @@ class Project < ActiveRecord::Base has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :external_wiki_service, dependent: :destroy has_one :kubernetes_service, dependent: :destroy, inverse_of: :project + has_one :mock_ci_service, dependent: :destroy has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" has_one :forked_from_project, through: :forked_project_link @@ -211,6 +212,7 @@ class Project < ActiveRecord::Base before_save :ensure_runners_token mount_uploader :avatar, AvatarUploader + has_many :uploads, as: :model, dependent: :destroy # Scopes default_scope { where(pending_delete: false) } @@ -334,7 +336,7 @@ class Project < ActiveRecord::Base end def search_by_visibility(level) - where(visibility_level: Gitlab::VisibilityLevel.const_get(level.upcase)) + where(visibility_level: Gitlab::VisibilityLevel.string_options[level]) end def search_by_title(query) @@ -1003,7 +1005,7 @@ class Project < ActiveRecord::Base end def visibility_level_field - visibility_level + :visibility_level end def archive! diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb index 9819e723fe8..f2e1c906dac 100644 --- a/app/models/project_services/kubernetes_service.rb +++ b/app/models/project_services/kubernetes_service.rb @@ -94,7 +94,12 @@ class KubernetesService < DeploymentService { key: 'KUBE_TOKEN', value: token, public: false }, { key: 'KUBE_NAMESPACE', value: namespace, public: true } ] - variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true } if ca_pem.present? + + if ca_pem.present? + variables << { key: 'KUBE_CA_PEM', value: ca_pem, public: true } + variables << { key: 'KUBE_CA_PEM_FILE', value: ca_pem, public: true, file: true } + end + variables end diff --git a/app/models/repository.rb b/app/models/repository.rb index 0dbf246c3a4..e7cc8d6e083 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -6,6 +6,7 @@ class Repository attr_accessor :path_with_namespace, :project CommitError = Class.new(StandardError) + CreateTreeError = Class.new(StandardError) # Methods that cache data from the Git repository. # @@ -862,17 +863,18 @@ class Repository end def revert( - user, commit, branch_name, revert_tree_id = nil, + user, commit, branch_name, start_branch_name: nil, start_project: project) - revert_tree_id ||= check_revert_content(commit, branch_name) - - return false unless revert_tree_id - GitOperationService.new(user, self).with_branch( branch_name, start_branch_name: start_branch_name, start_project: start_project) do |start_commit| + revert_tree_id = check_revert_content(commit, start_commit.sha) + unless revert_tree_id + raise Repository::CreateTreeError.new('Failed to revert commit') + end + committer = user_to_committer(user) Rugged::Commit.create(rugged, @@ -885,17 +887,18 @@ class Repository end def cherry_pick( - user, commit, branch_name, cherry_pick_tree_id = nil, + user, commit, branch_name, start_branch_name: nil, start_project: project) - cherry_pick_tree_id ||= check_cherry_pick_content(commit, branch_name) - - return false unless cherry_pick_tree_id - GitOperationService.new(user, self).with_branch( branch_name, start_branch_name: start_branch_name, start_project: start_project) do |start_commit| + cherry_pick_tree_id = check_cherry_pick_content(commit, start_commit.sha) + unless cherry_pick_tree_id + raise Repository::CreateTreeError.new('Failed to cherry-pick commit') + end + committer = user_to_committer(user) Rugged::Commit.create(rugged, @@ -919,9 +922,8 @@ class Repository end end - def check_revert_content(target_commit, branch_name) - source_sha = commit(branch_name).sha - args = [target_commit.sha, source_sha] + def check_revert_content(target_commit, source_sha) + args = [target_commit.sha, source_sha] args << { mainline: 1 } if target_commit.merge_commit? revert_index = rugged.revert_commit(*args) @@ -933,9 +935,8 @@ class Repository tree_id end - def check_cherry_pick_content(target_commit, branch_name) - source_sha = commit(branch_name).sha - args = [target_commit.sha, source_sha] + def check_cherry_pick_content(target_commit, source_sha) + args = [target_commit.sha, source_sha] args << 1 if target_commit.merge_commit? cherry_pick_index = rugged.cherrypick_commit(*args) @@ -995,6 +996,8 @@ class Repository end def with_repo_branch_commit(start_repository, start_branch_name) + return yield(nil) if start_repository.empty_repo? + branch_name_or_sha = if start_repository == self start_branch_name diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 2665a7249a3..dbd564e5e7d 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -120,7 +120,7 @@ class Snippet < ActiveRecord::Base end def visibility_level_field - visibility_level + :visibility_level end def no_highlighting? diff --git a/app/models/upload.rb b/app/models/upload.rb new file mode 100644 index 00000000000..13987931b05 --- /dev/null +++ b/app/models/upload.rb @@ -0,0 +1,63 @@ +class Upload < ActiveRecord::Base + # Upper limit for foreground checksum processing + CHECKSUM_THRESHOLD = 100.megabytes + + belongs_to :model, polymorphic: true + + validates :size, presence: true + validates :path, presence: true + validates :model, presence: true + validates :uploader, presence: true + + before_save :calculate_checksum, if: :foreground_checksum? + after_commit :schedule_checksum, unless: :foreground_checksum? + + def self.remove_path(path) + where(path: path).destroy_all + end + + def self.record(uploader) + remove_path(uploader.relative_path) + + create( + size: uploader.file.size, + path: uploader.relative_path, + model: uploader.model, + uploader: uploader.class.to_s + ) + end + + def absolute_path + return path unless relative_path? + + uploader_class.absolute_path(self) + end + + def calculate_checksum + return unless exist? + + self.checksum = Digest::SHA256.file(absolute_path).hexdigest + end + + def exist? + File.exist?(absolute_path) + end + + private + + def foreground_checksum? + size <= CHECKSUM_THRESHOLD + end + + def schedule_checksum + UploadChecksumWorker.perform_async(id) + end + + def relative_path? + !path.start_with?('/') + end + + def uploader_class + Object.const_get(uploader) + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 8443594c055..bd57904a2cd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -21,6 +21,7 @@ class User < ActiveRecord::Base default_value_for :can_create_team, false default_value_for :hide_no_ssh_key, false default_value_for :hide_no_password, false + default_value_for :project_view, :files attr_encrypted :otp_secret, key: Gitlab::Application.secrets.otp_key_base, @@ -94,6 +95,7 @@ class User < ActiveRecord::Base has_many :todos, dependent: :destroy has_many :notification_settings, dependent: :destroy has_many :award_emoji, dependent: :destroy + has_many :triggers, dependent: :destroy, class_name: 'Ci::Trigger', foreign_key: :owner_id has_many :assigned_issues, dependent: :nullify, foreign_key: :assignee_id, class_name: "Issue" has_many :assigned_merge_requests, dependent: :nullify, foreign_key: :assignee_id, class_name: "MergeRequest" @@ -189,6 +191,7 @@ class User < ActiveRecord::Base end mount_uploader :avatar, AvatarUploader + has_many :uploads, as: :model, dependent: :destroy # Scopes scope :admins, -> { where(admin: true) } |