diff options
Diffstat (limited to 'app/models/project.rb')
-rw-r--r-- | app/models/project.rb | 157 |
1 files changed, 122 insertions, 35 deletions
diff --git a/app/models/project.rb b/app/models/project.rb index 017471995ec..412c6c6732d 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -29,6 +29,14 @@ # import_source :string(255) # commit_count :integer default(0) # import_error :text +# ci_id :integer +# builds_enabled :boolean default(TRUE), not null +# shared_runners_enabled :boolean default(TRUE), not null +# runners_token :string +# build_coverage_regex :string +# build_allow_git_fetch :boolean default(TRUE), not null +# build_timeout :integer default(3600), not null +# pending_delete :boolean # require 'carrierwave/orm/activerecord' @@ -43,6 +51,7 @@ class Project < ActiveRecord::Base include Sortable include AfterCommitQueue include CaseSensitivity + include TokenAuthenticatable extend Gitlab::ConfigHelper @@ -81,6 +90,7 @@ class Project < ActiveRecord::Base acts_as_taggable_on :tags attr_accessor :new_default_branch + attr_accessor :old_path_with_namespace # Relations belongs_to :creator, foreign_key: 'creator_id', class_name: 'User' @@ -141,6 +151,9 @@ class Project < ActiveRecord::Base has_many :releases, dependent: :destroy has_many :lfs_objects_projects, dependent: :destroy has_many :lfs_objects, through: :lfs_objects_projects + has_many :project_group_links, dependent: :destroy + has_many :invited_groups, through: :project_group_links, source: :group + has_many :todos, dependent: :destroy has_one :import_data, dependent: :destroy, class_name: "ProjectImportData" @@ -185,10 +198,8 @@ class Project < ActiveRecord::Base if: ->(project) { project.avatar.present? && project.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } - before_validation :set_runners_token_token - def set_runners_token_token - self.runners_token = SecureRandom.hex(15) if self.runners_token.blank? - end + add_authentication_token_field :runners_token + before_save :ensure_runners_token mount_uploader :avatar, AvatarUploader @@ -207,6 +218,7 @@ class Project < ActiveRecord::Base scope :public_only, -> { where(visibility_level: Project::PUBLIC) } scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) } scope :non_archived, -> { where(archived: false) } + scope :for_milestones, ->(ids) { joins(:milestones).where('milestones.id' => ids).distinct } state_machine :import_status, initial: :none do event :import_start do @@ -242,12 +254,6 @@ class Project < ActiveRecord::Base where('projects.last_activity_at < ?', 6.months.ago) end - def publicish(user) - visibility_levels = [Project::PUBLIC] - visibility_levels << Project::INTERNAL if user - where(visibility_level: visibility_levels) - end - def with_push joins(:events).where('events.action = ?', Event::PUSHED) end @@ -256,17 +262,49 @@ class Project < ActiveRecord::Base joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') end + # Searches for a list of projects based on the query given in `query`. + # + # On PostgreSQL this method uses "ILIKE" to perform a case-insensitive + # search. On MySQL a regular "LIKE" is used as it's already + # case-insensitive. + # + # query - The search query as a String. def search(query) - joins(:namespace). - where('LOWER(projects.name) LIKE :query OR - LOWER(projects.path) LIKE :query OR - LOWER(namespaces.name) LIKE :query OR - LOWER(projects.description) LIKE :query', - query: "%#{query.try(:downcase)}%") + ptable = arel_table + ntable = Namespace.arel_table + pattern = "%#{query}%" + + projects = select(:id).where( + ptable[:path].matches(pattern). + or(ptable[:name].matches(pattern)). + or(ptable[:description].matches(pattern)) + ) + + # We explicitly remove any eager loading clauses as they're: + # + # 1. Not needed by this query + # 2. Combined with .joins(:namespace) lead to all columns from the + # projects & namespaces tables being selected, leading to a SQL error + # due to the columns of all UNION'd queries no longer being the same. + namespaces = select(:id). + except(:includes). + joins(:namespace). + where(ntable[:name].matches(pattern)) + + union = Gitlab::SQL::Union.new([projects, namespaces]) + + where("projects.id IN (#{union.to_sql})") + end + + def search_by_visibility(level) + where(visibility_level: Gitlab::VisibilityLevel.const_get(level.upcase)) end def search_by_title(query) - where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%") + pattern = "%#{query}%" + table = Project.arel_table + + non_archived.where(table[:name].matches(pattern)) end def find_with_namespace(id) @@ -330,13 +368,18 @@ class Project < ActiveRecord::Base end def repository - @repository ||= Repository.new(path_with_namespace, nil, self) + @repository ||= Repository.new(path_with_namespace, self) end def commit(id = 'HEAD') repository.commit(id) end + def merge_base_commit(first_commit_id, second_commit_id) + sha = repository.merge_base(first_commit_id, second_commit_id) + repository.commit(sha) if sha + end + def saved? id && persisted? end @@ -365,6 +408,10 @@ class Project < ActiveRecord::Base external_import? || forked? end + def no_import? + import_status == 'none' + end + def external_import? import_url.present? end @@ -390,7 +437,7 @@ class Project < ActiveRecord::Base result.password = '*****' unless result.password.nil? result.to_s rescue - original_url + self.import_url end def check_limit @@ -461,12 +508,10 @@ class Project < ActiveRecord::Base !external_issue_tracker end - def external_issues_trackers - services.select(&:issue_tracker?).reject(&:default?) - end - def external_issue_tracker - @external_issues_tracker ||= external_issues_trackers.find(&:activated?) + return @external_issue_tracker if defined?(@external_issue_tracker) + @external_issue_tracker ||= + services.issue_trackers.active.without_defaults.first end def can_have_issues_tracker_id? @@ -508,11 +553,11 @@ class Project < ActiveRecord::Base end def ci_services - services.select { |service| service.category == :ci } + services.where(category: :ci) end def ci_service - @ci_service ||= ci_services.find(&:activated?) + @ci_service ||= ci_services.reorder(nil).find_by(active: true) end def jira_tracker? @@ -526,10 +571,7 @@ class Project < ActiveRecord::Base end def avatar_in_git - @avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png') - @avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg') - @avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif') - @avatar_file + repository.avatar end def avatar_url @@ -693,6 +735,8 @@ class Project < ActiveRecord::Base old_path_with_namespace = File.join(namespace_dir, path_was) new_path_with_namespace = File.join(namespace_dir, path) + expire_caches_before_rename(old_path_with_namespace) + if gitlab_shell.mv_repository(old_path_with_namespace, new_path_with_namespace) # If repository moved successfully we need to send update instructions to users. # However we cannot allow rollback since we moved repository @@ -701,6 +745,11 @@ class Project < ActiveRecord::Base gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") send_move_instructions(old_path_with_namespace) reset_events_cache + + @old_path_with_namespace = old_path_with_namespace + + SystemHooksService.new.execute_hooks_for(self, :rename) + @repository = nil rescue # Returning false does not rollback after_* transaction but gives @@ -716,14 +765,39 @@ class Project < ActiveRecord::Base Gitlab::UploadsTransfer.new.rename_project(path_was, path, namespace.path) end + # Expires various caches before a project is renamed. + def expire_caches_before_rename(old_path) + repo = Repository.new(old_path, self) + wiki = Repository.new("#{old_path}.wiki", self) + + if repo.exists? + repo.expire_cache + repo.expire_emptiness_caches + end + + if wiki.exists? + wiki.expire_cache + wiki.expire_emptiness_caches + end + end + def hook_attrs { name: name, - ssh_url: ssh_url_to_repo, - http_url: http_url_to_repo, + description: description, web_url: web_url, + avatar_url: avatar_url, + git_ssh_url: ssh_url_to_repo, + git_http_url: http_url_to_repo, namespace: namespace.name, - visibility_level: visibility_level + 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 } end @@ -769,6 +843,7 @@ class Project < ActiveRecord::Base end def change_head(branch) + repository.before_change_head gitlab_shell.update_repository_head(self.path_with_namespace, branch) reload_default_branch end @@ -825,6 +900,10 @@ class Project < ActiveRecord::Base jira_tracker? && jira_service.active end + def allowed_to_share_with_group? + !namespace.share_with_group_lock + end + def ci_commit(sha) ci_commits.find_by(sha: sha) end @@ -856,13 +935,13 @@ class Project < ActiveRecord::Base end def valid_runners_token? token - self.runners_token && self.runners_token == token + self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token) end # TODO (ayufan): For now we use runners_token (backward compatibility) # In 8.4 every build will have its own individual token valid for time of build def valid_build_token? token - self.builds_enabled? && self.runners_token && self.runners_token == token + self.builds_enabled? && self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token) end def build_coverage_enabled? @@ -885,4 +964,12 @@ class Project < ActiveRecord::Base return true unless forked? Gitlab::VisibilityLevel.allowed_fork_levels(forked_from_project.visibility_level).include?(level.to_i) end + + def runners_token + ensure_runners_token! + end + + def wiki + @wiki ||= ProjectWiki.new(self, self.owner) + end end |