diff --git a/app/models/build_list.rb b/app/models/build_list.rb index ad749c2af..9448ec7c6 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -1,6 +1,6 @@ class BuildList < ActiveRecord::Base - include Modules::Models::CommitAndVersion - include Modules::Models::FileStoreClean + include CommitAndVersion + include FileStoreClean include AbfWorker::ModelHelper include Feed::BuildList include BuildListObserver @@ -164,7 +164,7 @@ class BuildList < ActiveRecord::Base after_transition on: [:published, :fail_publish, :build_error, :tests_failed], do: :notify_users after_transition on: :build_success, do: :notify_users, - unless: -> { |build_list| build_list.auto_publish? || build_list.auto_publish_into_testing? } + unless: ->(build_list) { build_list.auto_publish? || build_list.auto_publish_into_testing? } event :place_build do transition waiting_for_response: :build_pending diff --git a/app/models/comment.rb b/app/models/comment.rb index d674c38dc..10cf7ff0c 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -17,7 +17,7 @@ class Comment < ActiveRecord::Base scope :for_commit, ->(c) { where(commentable_id: c.id.hex, commentable_type: c.class) } default_scope { order(:created_at) } - after_create :subscribe_on_reply, unless: -> {|c| c.commit_comment?} + after_create :subscribe_on_reply, unless: ->(c) { c.commit_comment? } after_create :subscribe_users attr_accessible :body, :data diff --git a/app/models/concerns/acts_like_member.rb b/app/models/concerns/acts_like_member.rb new file mode 100644 index 000000000..a80088a59 --- /dev/null +++ b/app/models/concerns/acts_like_member.rb @@ -0,0 +1,37 @@ +module ActsLikeMember + extend ActiveSupport::Concern + + included do + scope :not_member_of, ->(item) { + where(" + #{table_name}.id NOT IN ( + SELECT relations.actor_id + FROM relations + WHERE ( + relations.actor_type = '#{self.to_s}' + AND relations.target_type = '#{item.class.to_s}' + AND relations.target_id = #{item.id} + ) + ) + ") + } + scope :search_order, -> { order('CHAR_LENGTH(#{table_name}.uname) ASC') } + scope :without, ->(a) { where("#{table_name}.id NOT IN (?)", a) } + scope :by_uname, ->(n) { where("#{table_name}.uname ILIKE ?", n) } + scope :search, ->(q) { by_uname("%#{q.to_s.strip}%") } + end + + def to_param + uname + end + + module ClassMethods + def find_by_insensitive_uname(uname) + find_by_uname(uname) || by_uname(uname).first + end + + def find_by_insensitive_uname!(uname) + find_by_insensitive_uname(uname) or raise ActiveRecord::RecordNotFound + end + end +end diff --git a/app/models/concerns/autostart.rb b/app/models/concerns/autostart.rb new file mode 100644 index 000000000..c9b66ee3c --- /dev/null +++ b/app/models/concerns/autostart.rb @@ -0,0 +1,31 @@ +module Autostart + extend ActiveSupport::Concern + + ONCE_A_12_HOURS = 0 + ONCE_A_DAY = 1 + ONCE_A_WEEK = 2 + + AUTOSTART_STATUSES = [ONCE_A_12_HOURS, ONCE_A_DAY, ONCE_A_WEEK] + HUMAN_AUTOSTART_STATUSES = { + ONCE_A_12_HOURS => :once_a_12_hours, + ONCE_A_DAY => :once_a_day, + ONCE_A_WEEK => :once_a_week + } + + included do + validates :autostart_status, numericality: true, + inclusion: {in: AUTOSTART_STATUSES}, allow_blank: true + + attr_accessible :autostart_status + end + + def human_autostart_status + self.class.human_autostart_status(autostart_status) + end + + module ClassMethods + def human_autostart_status(autostart_status) + I18n.t("layout.products.autostart_statuses.#{HUMAN_AUTOSTART_STATUSES[autostart_status]}") + end + end +end diff --git a/app/models/concerns/build_list_observer.rb b/app/models/concerns/build_list_observer.rb index 8061a3c54..b99411764 100644 --- a/app/models/concerns/build_list_observer.rb +++ b/app/models/concerns/build_list_observer.rb @@ -32,5 +32,4 @@ module BuildListObserver end end end - end diff --git a/app/models/concerns/commit_and_version.rb b/app/models/concerns/commit_and_version.rb new file mode 100644 index 000000000..e2a965841 --- /dev/null +++ b/app/models/concerns/commit_and_version.rb @@ -0,0 +1,33 @@ +module CommitAndVersion + extend ActiveSupport::Concern + + included do + + validate -> { + if project && (commit_hash.blank? || project.repo.commit(commit_hash).blank?) + errors.add :commit_hash, I18n.t('flash.build_list.wrong_commit_hash', commit_hash: commit_hash) + end + } + + before_validation :set_commit_and_version + before_create :set_last_published_commit + end + + protected + + def set_commit_and_version + if project && project_version.present? && commit_hash.blank? + self.commit_hash = project.repo.commits(project_version).try(:first).try(:id) + elsif project_version.blank? && commit_hash.present? + self.project_version = commit_hash + end + end + + def set_last_published_commit + return unless self.respond_to? :last_published_commit_hash # product? + last_commit = self.last_published.first.try :commit_hash + if last_commit && self.project.repo.commit(last_commit).present? # commit(nil) is not nil! + self.last_published_commit_hash = last_commit + end + end +end diff --git a/app/models/concerns/feed/issue.rb b/app/models/concerns/feed/issue.rb index b5d090b1e..df0ee1a28 100644 --- a/app/models/concerns/feed/issue.rb +++ b/app/models/concerns/feed/issue.rb @@ -4,11 +4,11 @@ module Feed::Issue included do after_commit :new_issue_notifications, on: :create - after_commit :send_assign_notifications, on: :create, if: Proc.new { |i| i.assignee } + after_commit :send_assign_notifications, on: :create, if: -> { |i| i.assignee } after_commit -> { send_assign_notifications(:update) }, on: :update after_commit :send_hooks, on: :create - after_commit -> { send_hooks(:update) }, on: :update, if: Proc.new { |i| i.previous_changes['status'].present? } + after_commit -> { send_hooks(:update) }, on: :update, if: -> { |i| i.previous_changes['status'].present? } end private diff --git a/app/models/concerns/file_store_clean.rb b/app/models/concerns/file_store_clean.rb new file mode 100644 index 000000000..afced4ff9 --- /dev/null +++ b/app/models/concerns/file_store_clean.rb @@ -0,0 +1,49 @@ +module FileStoreClean + extend ActiveSupport::Concern + + included do + def destroy + destroy_files_from_file_store if Rails.env.production? + super + end + later :destroy, queue: :clone_build + + def sha1_of_file_store_files + raise NotImplementedError, "You should implement this method" + end + + def destroy_files_from_file_store(args = sha1_of_file_store_files) + files = *args + token = User.find_by_uname('file_store').authentication_token + uri = URI APP_CONFIG['file_store_url'] + Net::HTTP.start(uri.host, uri.port) do |http| + files.each do |sha1| + begin + req = Net::HTTP::Delete.new("/api/v1/file_stores/#{sha1}.json") + req.basic_auth token, '' + http.request(req) + rescue # Dont care about it + end + end + end + end + + def later_destroy_files_from_file_store(args) + destroy_files_from_file_store(args) + end + later :later_destroy_files_from_file_store, queue: :clone_build + end + + def self.file_exist_on_file_store?(sha1) + begin + resp = JSON(RestClient.get "#{APP_CONFIG['file_store_url']}/api/v1/file_stores.json", params: {hash: sha1}) + rescue # Dont care about it + resp = [] + end + if resp[0].respond_to?('[]') && resp[0]['file_name'] && resp[0]['sha1_hash'] + true + else + false + end + end +end diff --git a/app/models/concerns/git.rb b/app/models/concerns/git.rb new file mode 100644 index 000000000..64ee9947d --- /dev/null +++ b/app/models/concerns/git.rb @@ -0,0 +1,241 @@ +require 'nokogiri' +require 'open-uri' + +module Git + extend ActiveSupport::Concern + + included do + has_attached_file :srpm + + validates_attachment_size :srpm, less_than_or_equal_to: 500.megabytes + validates_attachment_content_type :srpm, content_type: ['application/octet-stream', "application/x-rpm", "application/x-redhat-package-manager"], message: I18n.t('layout.invalid_content_type') + + after_create :create_git_repo + after_commit(on: :create) {|p| p.fork_git_repo unless p.is_root?} # later with resque + after_commit(on: :create) {|p| p.import_attached_srpm if p.srpm?} # later with resque # should be after create_git_repo + after_destroy :destroy_git_repo + # after_rollback -> { destroy_git_repo rescue true if new_record? } + + later :import_attached_srpm, queue: :fork_import + later :fork_git_repo, queue: :fork_import + end + + def repo + @repo ||= Grit::Repo.new(path) rescue Grit::Repo.new(GAP_REPO_PATH) + end + + def path + build_path(name_with_owner) + end + + def versions + repo.tags.map(&:name) + repo.branches.map(&:name) + end + + def create_branch(new_ref, from_ref, user) + return false if new_ref.blank? || from_ref.blank? || !(from_commit = repo.commit(from_ref)) + status, out, err = repo.git.native(:branch, {process_info: true}, new_ref, from_commit.id) + if status == 0 + Resque.enqueue(GitHook, owner.uname, name, from_commit.id, GitHook::ZERO, "refs/heads/#{new_ref}", 'commit', "user-#{user.id}", nil) + return true + end + return false + + end + + def delete_branch(branch, user) + return false if default_branch == branch.name + message = repo.git.native(:branch, {}, '-D', branch.name) + if message.present? + Resque.enqueue(GitHook, owner.uname, name, GitHook::ZERO, branch.commit.id, "refs/heads/#{branch.name}", 'commit', "user-#{user.id}", message) + end + return message.present? + end + + def update_file(path, data, options = {}) + head = options[:head].to_s || default_branch + actor = get_actor(options[:actor]) + filename = File.split(path).last + message = options[:message] + message = "Updated file #{filename}" if message.nil? or message.empty? + + # can not write to unexisted branch + return false if repo.branches.select{|b| b.name == head}.size != 1 + + parent = repo.commits(head).first + + index = repo.index + index.read_tree(parent.tree.id) + + # can not create new file + return false if (index.current_tree / path).nil? + + system "sudo chown -R rosa:rosa #{repo.path}" #FIXME Permission denied - /mnt/gitstore/git_projects/... + index.add(path, data) + if sha1 = index.commit(message, parents: [parent], actor: actor, last_tree: parent.tree.id, head: head) + Resque.enqueue(GitHook, owner.uname, name, sha1, sha1, "refs/heads/#{head}", 'commit', "user-#{options[:actor].id}", message) + end + sha1 + end + + def paginate_commits(treeish, options = {}) + options[:page] = options[:page].try(:to_i) || 1 + options[:per_page] = options[:per_page].try(:to_i) || 20 + + skip = options[:per_page] * (options[:page] - 1) + last_page = (skip + options[:per_page]) >= repo.commit_count(treeish) + + [repo.commits(treeish, options[:per_page], skip), options[:page], last_page] + end + + def tree_info(tree, treeish = nil, path = nil) + return [] unless tree + grouped = tree.contents.sort_by{|c| c.name.downcase}.group_by(&:class) + [ + grouped[Grit::Tree], + grouped[Grit::Blob], + grouped[Grit::Submodule] + ].compact.flatten.map do |node| + node_path = File.join([path.present? ? path : nil, node.name].compact) + [ + node, + node_path, + repo.log(treeish, node_path, max_count: 1).first + ] + end + end + + def import_srpm(srpm_path = srpm.path, branch_name = 'import') + token = User.find_by_uname('rosa_system').authentication_token + opts = [srpm_path, path, branch_name, Rails.root.join('bin', 'file-store.rb'), token, APP_CONFIG['file_store_url']].join(' ') + system("#{Rails.root.join('bin', 'import_srpm.sh')} #{opts} >> /dev/null 2>&1") + end + + def is_empty? + repo.branches.count == 0 + end + + def total_commits_count + return 0 if is_empty? + %x(cd #{path} && git rev-list --all | wc -l).to_i + end + + protected + + def build_path(dir) + File.join(APP_CONFIG['git_path'], 'git_projects', "#{dir}.git") + end + + def import_attached_srpm + if srpm? + import_srpm # srpm.path + self.srpm = nil; save # clear srpm + end + end + + def create_git_repo + if is_root? + Grit::Repo.init_bare(path) + write_hook + end + end + + def fork_git_repo + dummy = Grit::Repo.new(path) rescue parent.repo.fork_bare(path, shared: false) + write_hook + end + + def destroy_git_repo + FileUtils.rm_rf path + end + + def write_hook + hook = "/home/#{APP_CONFIG['shell_user']}/gitlab-shell/hooks/post-receive" + hook_file = File.join(path, 'hooks', 'post-receive') + FileUtils.ln_sf hook, hook_file + end + + def get_actor(actor = nil) + @last_actor = case actor.class.to_s + when 'Grit::Actor' then options[:actor] + when 'Hash' then Grit::Actor.new(actor[:name], actor[:email]) + when 'String' then Grit::Actor.from_stirng(actor) + else begin + if actor.respond_to?(:name) and actor.respond_to?(:email) + Grit::Actor.new(actor.name, actor.email) + else + config = Grit::Config.new(repo) + Grit::Actor.new(config['user.name'], config['user.email']) + end + end + end + @last_actor + end + + module ClassMethods + MAX_SRC_SIZE = 1024*1024*256 + + def process_hook(owner_uname, repo, newrev, oldrev, ref, newrev_type, user = nil, message = nil) + rec = GitHook.new(owner_uname, repo, newrev, oldrev, ref, newrev_type, user, message) + Modules::Observers::ActivityFeed::Git.create_notifications rec + end + + def run_mass_import(url, srpms_list, visibility, owner, add_to_repository_id) + doc = Nokogiri::HTML(open(url)) + links = doc.css("a[href$='.src.rpm']") + return if links.count == 0 + filter = srpms_list.lines.map(&:chomp).map(&:strip).select(&:present?) + + repository = Repository.find add_to_repository_id + platform = repository.platform + dir = Dir.mktmpdir 'mass-import-', APP_CONFIG['tmpfs_path'] + links.each do |link| + begin + package = link.attributes['href'].value + package.chomp!; package.strip! + + next if package.size == 0 || package !~ /^[\w\.\-]+$/ + next if filter.present? && !filter.include?(package) + + uri = URI "#{url}/#{package}" + srpm_file = "#{dir}/#{package}" + Net::HTTP.start(uri.host) do |http| + if http.request_head(uri.path)['content-length'].to_i < MAX_SRC_SIZE + f = open(srpm_file, 'wb') + http.request_get(uri.path) do |resp| + resp.read_body{ |segment| f.write(segment) } + end + f.close + end + end + if name = `rpm -q --qf '[%{Name}]' -p #{srpm_file}` and $?.success? and name.present? + next if owner.projects.exists?(name: name) + description = `rpm -q --qf '[%{Description}]' -p #{srpm_file}`.scrub('') + + project = owner.projects.build( + name: name, + description: description, + visibility: visibility, + is_package: false # See: Hook for #attach_to_personal_repository + ) + project.owner = owner + if project.save + repository.projects << project rescue nil + project.update_attributes(is_package: true) + project.import_srpm srpm_file, platform.name + end + end + rescue => e + f.close if defined?(f) + Airbrake.notify_or_ignore(e, link: link.to_s, url: url, owner: owner) + ensure + File.delete srpm_file if srpm_file + end + end + rescue => e + Airbrake.notify_or_ignore(e, url: url, owner: owner) + ensure + FileUtils.remove_entry_secure dir if dir + end + end +end diff --git a/app/models/concerns/markdown.rb b/app/models/concerns/markdown.rb new file mode 100644 index 000000000..bb8a51691 --- /dev/null +++ b/app/models/concerns/markdown.rb @@ -0,0 +1,190 @@ +# This module is based on +# https://github.com/gitlabhq/gitlabhq/blob/397c3da9758c03a215a308c011f94261d9c61cfa/lib/gitlab/markdown.rb + +# Custom parser for GitLab-flavored Markdown +# +# It replaces references in the text with links to the appropriate items in +# GitLab. +# +# Supported reference formats are: +# * @foo for team members +# * for issues & pull requests: +# * #123 +# * abf#123 +# * abf/rosa-build#123 +# * 123456 for commits +# +# It also parses Emoji codes to insert images. See +# http://www.emoji-cheat-sheet.com/ for a list of the supported icons. +# +# Examples +# +# >> gfm("Hey @david, can you fix this?") +# => "Hey @david, can you fix this?" +# +# >> gfm("Commit 35d5f7c closes #1234") +# => "Commit 35d5f7c closes #1234" +# +# >> gfm(":trollface:") +# => "\":trollface:\" +module Markdown + include IssuesHelper + + attr_reader :html_options + + # Public: Parse the provided text with GitLab-Flavored Markdown + # + # text - the source text + # html_options - extra options for the reference links as given to link_to + # + # Note: reference links will only be generated if @project is set + def gfm(text, html_options = {}) + return text if text.nil? + + # Duplicate the string so we don't alter the original, then call to_str + # to cast it back to a String instead of a SafeBuffer. This is required + # for gsub calls to work as we need them to. + text = text.dup.to_str + + @html_options = html_options + + # Extract pre blocks so they are not altered + # from http://github.github.com/github-flavored-markdown/ + text.gsub!(%r{
.*?
|.*?}m) { |match| extract_piece(match) } + # Extract links with probably parsable hrefs + text.gsub!(%r{.*?}m) { |match| extract_piece(match) } + # Extract images with probably parsable src + text.gsub!(%r{}m) { |match| extract_piece(match) } + + # TODO: add popups with additional information + + text = parse(text) + + # Insert pre block extractions + text.gsub!(/\{gfm-extraction-(\h{32})\}/) do + insert_piece($1) + end + + sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class) + end + + private + + def extract_piece(text) + @extractions ||= {} + + md5 = Digest::MD5.hexdigest(text) + @extractions[md5] = text + "{gfm-extraction-#{md5}}" + end + + def insert_piece(id) + @extractions[id] + end + + # Private: Parses text for references and emoji + # + # text - Text to parse + # + # Note: reference links will only be generated if @project is set + # + # Returns parsed text + def parse(text) + parse_references(text) if @project + parse_emoji(text) + + text + end + + REFERENCE_PATTERN = %r{ + (?[\W\/])? # Prefix + ( # Reference + @(?[a-zA-Z][a-zA-Z0-9_\-\.]*) # User/Group uname + |(?(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?\#[0-9]+) # Issue ID + |(?[\h]{6,40}) # Commit ID + ) + (?\W)? # Suffix + }x.freeze + + TYPES = [:user, :issue, :commit].freeze + + def parse_references(text) + # parse reference links + text.gsub!(REFERENCE_PATTERN) do |match| + prefix = $~[:prefix] + suffix = $~[:suffix] + type = TYPES.select{|t| !$~[t].nil?}.first + identifier = $~[type] + + # Avoid HTML entities + if prefix && suffix && prefix[0] == '&' && suffix[-1] == ';' + match + elsif ref_link = reference_link(type, identifier) + "#{prefix}#{ref_link}#{suffix}" + else + match + end + end + end + + EMOJI_PATTERN = %r{(:(\S+):)}.freeze + + def parse_emoji(text) + # parse emoji + text.gsub!(EMOJI_PATTERN) do |match| + if valid_emoji?($2) + image_tag(image_path("emoji/#{$2}.png"), class: 'emoji', title: $1, alt: $1, size: "20x20") + else + match + end + end + end + + # Private: Checks if an emoji icon exists in the image asset directory + # + # emoji - Identifier of the emoji as a string (e.g., "+1", "heart") + # + # Returns boolean + def valid_emoji?(emoji) + Emoji.names.include? emoji + end + + # Private: Dispatches to a dedicated processing method based on reference + # + # reference - Object reference ("@1234", "!567", etc.) + # identifier - Object identifier (Issue ID, SHA hash, etc.) + # + # Returns string rendered by the processing method + def reference_link(type, identifier) + send("reference_#{type}", identifier) + end + + def reference_user(identifier) + member = User.where(uname: identifier).first || Group.where(uname: identifier).first + if member + link_to("@#{identifier}", "/#{identifier}", html_options.merge(title: member.fullname, class: "gfm gfm-member #{html_options[:class]}")) + end + end + + def reference_issue(identifier) + if issue = Issue.find_by_hash_tag(identifier, current_ability, @project) + if issue.pull_request + title = "#{PullRequest.model_name.human}: #{issue.title}" + url = project_pull_request_path(issue.project, issue.pull_request) + else + title = "#{Issue.model_name.human}: #{issue.title}" + url = project_issue_path(issue.project.owner, issue.project.name, issue.serial_id) + end + link_to(identifier, url, html_options.merge(title: title, class: "gfm gfm-issue #{html_options[:class]}")) + end + end + + def reference_commit(identifier) + if commit = @project.repo.commit(identifier) + link_to shortest_hash_id(commit.id), commit_path(@project, commit.id) + title = GitPresenters::CommitAsMessagePresenter.present(commit, project: @project) do |presenter| + link_to(identifier, commit_path(@project, commit), html_options.merge(title: presenter.caption, class: "gfm gfm-commit #{html_options[:class]}")) + end + end + end +end diff --git a/app/models/concerns/owner.rb b/app/models/concerns/owner.rb new file mode 100644 index 000000000..c7d2d489a --- /dev/null +++ b/app/models/concerns/owner.rb @@ -0,0 +1,8 @@ +module Owner + extend ActiveSupport::Concern + + included do + validates :owner, presence: true + after_create -> { relations.create actor_id: owner.id, actor_type: owner.class.to_s, role: 'admin' } + end +end diff --git a/app/models/concerns/personal_repository.rb b/app/models/concerns/personal_repository.rb new file mode 100644 index 000000000..ed03d9094 --- /dev/null +++ b/app/models/concerns/personal_repository.rb @@ -0,0 +1,37 @@ +module PersonalRepository + extend ActiveSupport::Concern + + included do + after_create :create_personal_repository, unless: :system? + end + + def create_personal_repository + begin + pl = own_platforms.build + pl.owner = self + pl.name = "#{self.uname}_personal" + pl.description = "#{self.uname}_personal" + pl.platform_type = 'personal' + pl.distrib_type = APP_CONFIG['distr_types'].first + pl.visibility = 'open' + pl.save! + + rep = pl.repositories.build + rep.name = 'main' + rep.description = 'main' + rep.save! + rescue Exception => e + pl.now_destroy rescue false + raise e + end + return true + end + + def personal_platform + own_platforms.personal.first + end + + def personal_repository + personal_platform.repositories.first + end +end diff --git a/app/models/concerns/regeneration_status.rb b/app/models/concerns/regeneration_status.rb new file mode 100644 index 000000000..69920a599 --- /dev/null +++ b/app/models/concerns/regeneration_status.rb @@ -0,0 +1,44 @@ +module RegenerationStatus + extend ActiveSupport::Concern + + READY = 0 + WAITING_FOR_REGENERATION = 100 + REGENERATING = 200 + + HUMAN_STATUSES = { + READY => :ready, + WAITING_FOR_REGENERATION => :waiting_for_regeneration, + REGENERATING => :regenerating + } + + HUMAN_REGENERATION_STATUSES = { + AbfWorker::BaseObserver::COMPLETED => :completed, + AbfWorker::BaseObserver::FAILED => :failed, + AbfWorker::BaseObserver::CANCELED => :canceled + }.freeze + + included do + after_update :cleanup_file_store + + def sha1_of_file_store_files + files = [] + files << last_regenerated_log_sha1 if last_regenerated_log_sha1.present? + files + end + + def human_regeneration_status + self.class::HUMAN_REGENERATION_STATUSES[last_regenerated_status] || :no_data + end + + def human_status + self.class::HUMAN_STATUSES[status] || :no_data + end + + def cleanup_file_store + old_log_sha1 = last_regenerated_log_sha1_was + if old_log_sha1.present? && old_log_sha1 != last_regenerated_log_sha1 + later_destroy_files_from_file_store([old_log_sha1]) + end + end + end +end diff --git a/app/models/concerns/time_living.rb b/app/models/concerns/time_living.rb new file mode 100644 index 000000000..05f522573 --- /dev/null +++ b/app/models/concerns/time_living.rb @@ -0,0 +1,29 @@ +module TimeLiving + extend ActiveSupport::Concern + + included do + + validates :time_living, numericality: { only_integer: true }, presence: true + + validate -> { + # MIN_TIME_LIVING <= time_living <= MAX_TIME_LIVING or + # 2 min <= time_living <= 12 hours + # time_living in seconds + min = self.class.const_defined?(:MIN_TIME_LIVING) ? self.class::MIN_TIME_LIVING : 120 + max = self.class.const_defined?(:MAX_TIME_LIVING) ? self.class::MAX_TIME_LIVING : 43200 + if min > time_living.to_i || time_living.to_i > max + errors.add :time_living, + I18n.t('flash.time_living.numericality_error', min: (min / 60), max: (max / 60)) + end + } + + before_validation :convert_time_living + attr_accessible :time_living + end + + protected + + def convert_time_living + self.time_living = time_living.to_i * 60 if time_living_was.to_i != time_living.to_i + end +end diff --git a/lib/modules/models/url_helper.rb b/app/models/concerns/url_helper.rb similarity index 78% rename from lib/modules/models/url_helper.rb rename to app/models/concerns/url_helper.rb index c374899c3..318c1e7b8 100644 --- a/lib/modules/models/url_helper.rb +++ b/app/models/concerns/url_helper.rb @@ -1,7 +1,7 @@ -module Modules::Models::UrlHelper +module UrlHelper def default_url_options host ||= EventLog.current_controller.request.host_with_port rescue ::Rosa::Application.config.action_mailer.default_url_options[:host] protocol ||= APP_CONFIG['mailer_https_url'] ? 'https' : 'http' rescue 'http' - {host: host, protocol: protocol} + { host: host, protocol: protocol } end end diff --git a/lib/modules/models/web_hooks.rb b/app/models/concerns/web_hooks.rb similarity index 96% rename from lib/modules/models/web_hooks.rb rename to app/models/concerns/web_hooks.rb index 1911a30fc..0a75a5c0b 100644 --- a/lib/modules/models/web_hooks.rb +++ b/app/models/concerns/web_hooks.rb @@ -1,4 +1,4 @@ -module Modules::Models::WebHooks +module WebHooks class << self protected @@ -45,10 +45,11 @@ module Modules::Models::WebHooks password :password boolean :ssl, :message_without_join, :no_colors, :long_url, :notice end + add_hook :jabber do string :user end + SCHEMA.freeze NAMES.freeze - end diff --git a/app/models/concerns/wiki.rb b/app/models/concerns/wiki.rb new file mode 100644 index 000000000..47e852dfe --- /dev/null +++ b/app/models/concerns/wiki.rb @@ -0,0 +1,31 @@ +module Wiki + extend ActiveSupport::Concern + + included do + after_save :create_wiki + after_destroy :destroy_wiki + end + + def wiki_path + build_path(wiki_repo_name) + end + + def wiki_repo_name + File.join owner.uname, "#{name}.wiki" + end + + protected + + def create_wiki + if has_wiki && !FileTest.exist?(wiki_path) + Grit::Repo.init_bare(wiki_path) + wiki = Gollum::Wiki.new(wiki_path, {base_path: Rails.application.routes.url_helpers.project_wiki_index_path(owner, self)}) + wiki.write_page('Home', :markdown, I18n.t("wiki.seed.welcome_content"), + {name: owner.name, email: owner.email, message: 'Initial commit'}) + end + end + + def destroy_wiki + FileUtils.rm_rf wiki_path + end +end diff --git a/app/models/group.rb b/app/models/group.rb index 71ad3fa54..f6e3574fe 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -1,6 +1,6 @@ class Group < Avatar - include Modules::Models::ActsLikeMember - include Modules::Models::PersonalRepository + include ActsLikeMember + include PersonalRepository belongs_to :owner, class_name: 'User' diff --git a/app/models/hook.rb b/app/models/hook.rb index 755e4147c..5d2c16ec8 100644 --- a/app/models/hook.rb +++ b/app/models/hook.rb @@ -1,6 +1,6 @@ class Hook < ActiveRecord::Base - include Modules::Models::WebHooks - include Modules::Models::UrlHelper + include WebHooks + include UrlHelper include Rails.application.routes.url_helpers belongs_to :project @@ -121,7 +121,7 @@ class Hook < ActiveRecord::Base def cleanup_data if self.name.present? && fields = SCHEMA[self.name.to_sym] new_data = {} - fields.each{ |type, field| new_data[field] = self.data[field] } + fields.each { |type, field| new_data[field] = self.data[field] } self.data = new_data end end @@ -140,7 +140,7 @@ class Hook < ActiveRecord::Base modified << diff.a_path end end - {removed: removed, added: added, modified: modified} + { removed: removed, added: added, modified: modified } end end diff --git a/app/models/platform.rb b/app/models/platform.rb index 7cf7449b0..2cf877c8a 100644 --- a/app/models/platform.rb +++ b/app/models/platform.rb @@ -2,9 +2,9 @@ class Platform < ActiveRecord::Base extend FriendlyId friendly_id :name - include Modules::Models::FileStoreClean - include Modules::Models::RegenerationStatus - include Modules::Models::Owner + include FileStoreClean + include RegenerationStatus + include Owner include EventLoggable AUTOMATIC_METADATA_REGENERATIONS = %w(day week) @@ -32,10 +32,10 @@ class Platform < ActiveRecord::Base has_many :mass_builds, foreign_key: :save_to_platform_id validates :description, presence: true - validates :visibility, presence: true, inclusion: {in: VISIBILITIES} - validates :automatic_metadata_regeneration, inclusion: {in: AUTOMATIC_METADATA_REGENERATIONS}, allow_blank: true + validates :visibility, presence: true, inclusion: { in: VISIBILITIES } + validates :automatic_metadata_regeneration, inclusion: { in: AUTOMATIC_METADATA_REGENERATIONS }, allow_blank: true validates :name, uniqueness: {case_sensitive: false}, presence: true, format: { with: /\A#{NAME_PATTERN}\z/ } - validates :distrib_type, presence: true, inclusion: {in: APP_CONFIG['distr_types']} + validates :distrib_type, presence: true, inclusion: { in: APP_CONFIG['distr_types'] } validate -> { if released_was && !released errors.add(:released, I18n.t('flash.platform.released_status_can_not_be_changed')) @@ -79,7 +79,7 @@ class Platform < ActiveRecord::Base end event :regenerate do - transition ready: :waiting_for_regeneration, if: -> { |p| p.main? } + transition ready: :waiting_for_regeneration, if: ->(p) { p.main? } end event :start_regeneration do diff --git a/app/models/platform_arch_setting.rb b/app/models/platform_arch_setting.rb index 6d0dbd9fc..f9c1314aa 100644 --- a/app/models/platform_arch_setting.rb +++ b/app/models/platform_arch_setting.rb @@ -2,7 +2,7 @@ class PlatformArchSetting < ActiveRecord::Base DEFAULT_TIME_LIVING = 43200 # seconds, 12 hours MIN_TIME_LIVING = 600 # seconds, 10 minutes MAX_TIME_LIVING = 360000 # seconds, 100 hours, 4 day and 4 hours - include Modules::Models::TimeLiving + include TimeLiving belongs_to :arch belongs_to :platform diff --git a/app/models/product.rb b/app/models/product.rb index a920545df..25fa00542 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -1,6 +1,6 @@ class Product < ActiveRecord::Base - include Modules::Models::TimeLiving - include Modules::Models::Autostart + include TimeLiving + include Autostart include EventLoggable belongs_to :platform @@ -25,7 +25,7 @@ class Product < ActiveRecord::Base def full_clone(attrs = {}) dup.tap do |c| attrs.each {|k,v| c.send("#{k}=", v)} - c.time_living = c.time_living.to_i / 60 # see: Modules::Models::TimeLiving#convert_time_living + c.time_living = c.time_living.to_i / 60 # see: TimeLiving#convert_time_living c.platform_id = nil c.product_build_lists = [] c.updated_at = nil; c.created_at = nil @@ -33,7 +33,7 @@ class Product < ActiveRecord::Base end class << self - Modules::Models::Autostart::HUMAN_AUTOSTART_STATUSES.each do |autostart_status, human_autostart_status| + Autostart::HUMAN_AUTOSTART_STATUSES.each do |autostart_status, human_autostart_status| define_method "autostart_iso_builds_#{human_autostart_status}" do autostart_iso_builds autostart_status end diff --git a/app/models/product_build_list.rb b/app/models/product_build_list.rb index 67313c4e2..364f820fb 100644 --- a/app/models/product_build_list.rb +++ b/app/models/product_build_list.rb @@ -1,8 +1,8 @@ class ProductBuildList < ActiveRecord::Base - include Modules::Models::CommitAndVersion - include Modules::Models::TimeLiving - include Modules::Models::FileStoreClean - include Modules::Models::UrlHelper + include CommitAndVersion + include TimeLiving + include FileStoreClean + include UrlHelper include AbfWorker::ModelHelper include EventLoggable diff --git a/app/models/project.rb b/app/models/project.rb index 910c5cba8..c793124ed 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1,9 +1,9 @@ class Project < ActiveRecord::Base - include Modules::Models::Autostart - include Modules::Models::Owner - include Modules::Models::Git - include Modules::Models::Wiki - include Modules::Models::UrlHelper + include Autostart + include Owner + include Git + include Wiki + include UrlHelper include EventLoggable VISIBILITIES = ['open', 'hidden'] @@ -238,11 +238,11 @@ class Project < ActiveRecord::Base format_id = ProjectTag::FORMATS["#{tag_file_format(format)}"] project_tag = project_tags.where(tag_name: tag.name, format_id: format_id).first - return project_tag.sha1 if project_tag && project_tag.commit_id == tag.commit.id && Modules::Models::FileStoreClean.file_exist_on_file_store?(project_tag.sha1) + return project_tag.sha1 if project_tag && project_tag.commit_id == tag.commit.id && FileStoreClean.file_exist_on_file_store?(project_tag.sha1) archive = archive_by_treeish_and_format tag.name, format sha1 = Digest::SHA1.file(archive[:path]).hexdigest - unless Modules::Models::FileStoreClean.file_exist_on_file_store? sha1 + unless FileStoreClean.file_exist_on_file_store? sha1 token = User.find_by_uname('rosa_system').authentication_token begin resp = JSON `curl --user #{token}: -POST -F 'file_store[file]=@#{archive[:path]};filename=#{name}-#{tag.name}.#{tag_file_format(format)}' #{APP_CONFIG['file_store_url']}/api/v1/upload` @@ -297,7 +297,7 @@ class Project < ActiveRecord::Base end class << self - Modules::Models::Autostart::HUMAN_AUTOSTART_STATUSES.each do |autostart_status, human_autostart_status| + Autostart::HUMAN_AUTOSTART_STATUSES.each do |autostart_status, human_autostart_status| define_method "autostart_build_lists_#{human_autostart_status}" do autostart_build_lists autostart_status end diff --git a/app/models/project_import.rb b/app/models/project_import.rb index a34949833..ae1872a6a 100644 --- a/app/models/project_import.rb +++ b/app/models/project_import.rb @@ -7,5 +7,5 @@ class ProjectImport < ActiveRecord::Base scope :by_name, ->(name) { where("#{table_name}.name ILIKE ?", name) } - after_initialize -> {|r| r.file_mtime ||= Time.current - 10.years } # default + after_initialize ->(r) { r.file_mtime ||= Time.current - 10.years } # default end diff --git a/app/models/project_tag.rb b/app/models/project_tag.rb index 267bf39f2..3a116408d 100644 --- a/app/models/project_tag.rb +++ b/app/models/project_tag.rb @@ -1,5 +1,5 @@ class ProjectTag < ActiveRecord::Base - include Modules::Models::FileStoreClean + include FileStoreClean FORMATS = { 'zip' => 0, diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index 29250a897..1e98c5629 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -7,7 +7,7 @@ class PullRequest < ActiveRecord::Base :created_at, :updated_at, :comments, :status=, to: :issue, allow_nil: true validates :from_project, :to_project, presence: true - validate :uniq_merge, if: -> { |pull| pull.to_project.present? } + validate :uniq_merge, if: ->(pull) { pull.to_project.present? } validates_each :from_ref, :to_ref do |record, attr, value| check_ref record, attr, value end diff --git a/app/models/repository_status.rb b/app/models/repository_status.rb index 8b1359fc6..1f783d499 100644 --- a/app/models/repository_status.rb +++ b/app/models/repository_status.rb @@ -1,6 +1,6 @@ class RepositoryStatus < ActiveRecord::Base - include Modules::Models::FileStoreClean - include Modules::Models::RegenerationStatus + include FileStoreClean + include RegenerationStatus WAITING_FOR_RESIGN = 300 PUBLISH = 400 diff --git a/app/models/user.rb b/app/models/user.rb index 321362706..e4309a079 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,6 @@ class User < Avatar - include Modules::Models::PersonalRepository - include Modules::Models::ActsLikeMember + include PersonalRepository + include ActsLikeMember include Feed::User include EventLoggable diff --git a/lib/modules/models/acts_like_member.rb b/lib/modules/models/acts_like_member.rb deleted file mode 100644 index 372f4eed4..000000000 --- a/lib/modules/models/acts_like_member.rb +++ /dev/null @@ -1,42 +0,0 @@ -module Modules - module Models - module ActsLikeMember - extend ActiveSupport::Concern - - included do - scope :not_member_of, -> {|item| - where(" - #{table_name}.id NOT IN ( - SELECT relations.actor_id - FROM relations - WHERE ( - relations.actor_type = '#{self.to_s}' - AND relations.target_type = '#{item.class.to_s}' - AND relations.target_id = #{item.id} - ) - ) - ") - } - scope :search_order, { order('CHAR_LENGTH(#{table_name}.uname) ASC') } - scope :without, ->(a) { where("#{table_name}.id NOT IN (?)", a) } - scope :by_uname, ->(n) { where("#{table_name}.uname ILIKE ?", n) } - scope :search, ->(q) { by_uname("%#{q.to_s.strip}%") } - end - - def to_param - uname - end - - module ClassMethods - def find_by_insensitive_uname(uname) - find_by_uname(uname) || by_uname(uname).first - end - - def find_by_insensitive_uname!(uname) - find_by_insensitive_uname(uname) or raise ActiveRecord::RecordNotFound - end - end - end - end -end - diff --git a/lib/modules/models/autostart.rb b/lib/modules/models/autostart.rb deleted file mode 100644 index 397989b9a..000000000 --- a/lib/modules/models/autostart.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Modules - module Models - module Autostart - extend ActiveSupport::Concern - - ONCE_A_12_HOURS = 0 - ONCE_A_DAY = 1 - ONCE_A_WEEK = 2 - - AUTOSTART_STATUSES = [ONCE_A_12_HOURS, ONCE_A_DAY, ONCE_A_WEEK] - HUMAN_AUTOSTART_STATUSES = { - ONCE_A_12_HOURS => :once_a_12_hours, - ONCE_A_DAY => :once_a_day, - ONCE_A_WEEK => :once_a_week - } - - included do - validates :autostart_status, numericality: true, - inclusion: {in: AUTOSTART_STATUSES}, allow_blank: true - - attr_accessible :autostart_status - end - - def human_autostart_status - self.class.human_autostart_status(autostart_status) - end - - module ClassMethods - def human_autostart_status(autostart_status) - I18n.t("layout.products.autostart_statuses.#{HUMAN_AUTOSTART_STATUSES[autostart_status]}") - end - end - end - end -end \ No newline at end of file diff --git a/lib/modules/models/commit_and_version.rb b/lib/modules/models/commit_and_version.rb deleted file mode 100644 index fac090b08..000000000 --- a/lib/modules/models/commit_and_version.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Modules - module Models - module CommitAndVersion - extend ActiveSupport::Concern - - included do - - validate -> { - if project && (commit_hash.blank? || project.repo.commit(commit_hash).blank?) - errors.add :commit_hash, I18n.t('flash.build_list.wrong_commit_hash', commit_hash: commit_hash) - end - } - - before_validation :set_commit_and_version - before_create :set_last_published_commit - end - - protected - - def set_commit_and_version - if project && project_version.present? && commit_hash.blank? - self.commit_hash = project.repo.commits(project_version).try(:first).try(:id) - elsif project_version.blank? && commit_hash.present? - self.project_version = commit_hash - end - end - - def set_last_published_commit - return unless self.respond_to? :last_published_commit_hash # product? - last_commit = self.last_published.first.try :commit_hash - if last_commit && self.project.repo.commit(last_commit).present? # commit(nil) is not nil! - self.last_published_commit_hash = last_commit - end - end - end - end -end diff --git a/lib/modules/models/file_store_clean.rb b/lib/modules/models/file_store_clean.rb deleted file mode 100644 index 68b1ae2c3..000000000 --- a/lib/modules/models/file_store_clean.rb +++ /dev/null @@ -1,54 +0,0 @@ -module Modules - module Models - module FileStoreClean - extend ActiveSupport::Concern - - included do - def destroy - destroy_files_from_file_store if Rails.env.production? - super - end - later :destroy, queue: :clone_build - - def sha1_of_file_store_files - raise NotImplementedError, "You should implement this method" - end - - def destroy_files_from_file_store(args = sha1_of_file_store_files) - files = *args - token = User.find_by_uname('file_store').authentication_token - uri = URI APP_CONFIG['file_store_url'] - Net::HTTP.start(uri.host, uri.port) do |http| - files.each do |sha1| - begin - req = Net::HTTP::Delete.new("/api/v1/file_stores/#{sha1}.json") - req.basic_auth token, '' - http.request(req) - rescue # Dont care about it - end - end - end - end - - def later_destroy_files_from_file_store(args) - destroy_files_from_file_store(args) - end - later :later_destroy_files_from_file_store, queue: :clone_build - end - - def self.file_exist_on_file_store?(sha1) - begin - resp = JSON(RestClient.get "#{APP_CONFIG['file_store_url']}/api/v1/file_stores.json", params: {hash: sha1}) - rescue # Dont care about it - resp = [] - end - if resp[0].respond_to?('[]') && resp[0]['file_name'] && resp[0]['sha1_hash'] - true - else - false - end - end - - end - end -end diff --git a/lib/modules/models/git.rb b/lib/modules/models/git.rb deleted file mode 100644 index a1d56afa3..000000000 --- a/lib/modules/models/git.rb +++ /dev/null @@ -1,246 +0,0 @@ -require 'nokogiri' -require 'open-uri' - -module Modules - module Models - module Git - extend ActiveSupport::Concern - - included do - has_attached_file :srpm - - validates_attachment_size :srpm, less_than_or_equal_to: 500.megabytes - validates_attachment_content_type :srpm, content_type: ['application/octet-stream', "application/x-rpm", "application/x-redhat-package-manager"], message: I18n.t('layout.invalid_content_type') - - after_create :create_git_repo - after_commit(on: :create) {|p| p.fork_git_repo unless p.is_root?} # later with resque - after_commit(on: :create) {|p| p.import_attached_srpm if p.srpm?} # later with resque # should be after create_git_repo - after_destroy :destroy_git_repo - # after_rollback -> { destroy_git_repo rescue true if new_record? } - - later :import_attached_srpm, queue: :fork_import - later :fork_git_repo, queue: :fork_import - end - - def repo - @repo ||= Grit::Repo.new(path) rescue Grit::Repo.new(GAP_REPO_PATH) - end - - def path - build_path(name_with_owner) - end - - def versions - repo.tags.map(&:name) + repo.branches.map(&:name) - end - - def create_branch(new_ref, from_ref, user) - return false if new_ref.blank? || from_ref.blank? || !(from_commit = repo.commit(from_ref)) - status, out, err = repo.git.native(:branch, {process_info: true}, new_ref, from_commit.id) - if status == 0 - Resque.enqueue(GitHook, owner.uname, name, from_commit.id, GitHook::ZERO, "refs/heads/#{new_ref}", 'commit', "user-#{user.id}", nil) - return true - end - return false - - end - - def delete_branch(branch, user) - return false if default_branch == branch.name - message = repo.git.native(:branch, {}, '-D', branch.name) - if message.present? - Resque.enqueue(GitHook, owner.uname, name, GitHook::ZERO, branch.commit.id, "refs/heads/#{branch.name}", 'commit', "user-#{user.id}", message) - end - return message.present? - end - - def update_file(path, data, options = {}) - head = options[:head].to_s || default_branch - actor = get_actor(options[:actor]) - filename = File.split(path).last - message = options[:message] - message = "Updated file #{filename}" if message.nil? or message.empty? - - # can not write to unexisted branch - return false if repo.branches.select{|b| b.name == head}.size != 1 - - parent = repo.commits(head).first - - index = repo.index - index.read_tree(parent.tree.id) - - # can not create new file - return false if (index.current_tree / path).nil? - - system "sudo chown -R rosa:rosa #{repo.path}" #FIXME Permission denied - /mnt/gitstore/git_projects/... - index.add(path, data) - if sha1 = index.commit(message, parents: [parent], actor: actor, last_tree: parent.tree.id, head: head) - Resque.enqueue(GitHook, owner.uname, name, sha1, sha1, "refs/heads/#{head}", 'commit', "user-#{options[:actor].id}", message) - end - sha1 - end - - def paginate_commits(treeish, options = {}) - options[:page] = options[:page].try(:to_i) || 1 - options[:per_page] = options[:per_page].try(:to_i) || 20 - - skip = options[:per_page] * (options[:page] - 1) - last_page = (skip + options[:per_page]) >= repo.commit_count(treeish) - - [repo.commits(treeish, options[:per_page], skip), options[:page], last_page] - end - - def tree_info(tree, treeish = nil, path = nil) - return [] unless tree - grouped = tree.contents.sort_by{|c| c.name.downcase}.group_by(&:class) - [ - grouped[Grit::Tree], - grouped[Grit::Blob], - grouped[Grit::Submodule] - ].compact.flatten.map do |node| - node_path = File.join([path.present? ? path : nil, node.name].compact) - [ - node, - node_path, - repo.log(treeish, node_path, max_count: 1).first - ] - end - end - - def import_srpm(srpm_path = srpm.path, branch_name = 'import') - token = User.find_by_uname('rosa_system').authentication_token - opts = [srpm_path, path, branch_name, Rails.root.join('bin', 'file-store.rb'), token, APP_CONFIG['file_store_url']].join(' ') - system("#{Rails.root.join('bin', 'import_srpm.sh')} #{opts} >> /dev/null 2>&1") - end - - def is_empty? - repo.branches.count == 0 - end - - def total_commits_count - return 0 if is_empty? - %x(cd #{path} && git rev-list --all | wc -l).to_i - end - - protected - - def build_path(dir) - File.join(APP_CONFIG['git_path'], 'git_projects', "#{dir}.git") - end - - def import_attached_srpm - if srpm? - import_srpm # srpm.path - self.srpm = nil; save # clear srpm - end - end - - def create_git_repo - if is_root? - Grit::Repo.init_bare(path) - write_hook - end - end - - def fork_git_repo - dummy = Grit::Repo.new(path) rescue parent.repo.fork_bare(path, shared: false) - write_hook - end - - def destroy_git_repo - FileUtils.rm_rf path - end - - def write_hook - hook = "/home/#{APP_CONFIG['shell_user']}/gitlab-shell/hooks/post-receive" - hook_file = File.join(path, 'hooks', 'post-receive') - FileUtils.ln_sf hook, hook_file - end - - def get_actor(actor = nil) - @last_actor = case actor.class.to_s - when 'Grit::Actor' then options[:actor] - when 'Hash' then Grit::Actor.new(actor[:name], actor[:email]) - when 'String' then Grit::Actor.from_stirng(actor) - else begin - if actor.respond_to?(:name) and actor.respond_to?(:email) - Grit::Actor.new(actor.name, actor.email) - else - config = Grit::Config.new(repo) - Grit::Actor.new(config['user.name'], config['user.email']) - end - end - end - @last_actor - end - - module ClassMethods - MAX_SRC_SIZE = 1024*1024*256 - - def process_hook(owner_uname, repo, newrev, oldrev, ref, newrev_type, user = nil, message = nil) - rec = GitHook.new(owner_uname, repo, newrev, oldrev, ref, newrev_type, user, message) - Modules::Observers::ActivityFeed::Git.create_notifications rec - end - - def run_mass_import(url, srpms_list, visibility, owner, add_to_repository_id) - doc = Nokogiri::HTML(open(url)) - links = doc.css("a[href$='.src.rpm']") - return if links.count == 0 - filter = srpms_list.lines.map(&:chomp).map(&:strip).select(&:present?) - - repository = Repository.find add_to_repository_id - platform = repository.platform - dir = Dir.mktmpdir 'mass-import-', APP_CONFIG['tmpfs_path'] - links.each do |link| - begin - package = link.attributes['href'].value - package.chomp!; package.strip! - - next if package.size == 0 || package !~ /^[\w\.\-]+$/ - next if filter.present? && !filter.include?(package) - - uri = URI "#{url}/#{package}" - srpm_file = "#{dir}/#{package}" - Net::HTTP.start(uri.host) do |http| - if http.request_head(uri.path)['content-length'].to_i < MAX_SRC_SIZE - f = open(srpm_file, 'wb') - http.request_get(uri.path) do |resp| - resp.read_body{ |segment| f.write(segment) } - end - f.close - end - end - if name = `rpm -q --qf '[%{Name}]' -p #{srpm_file}` and $?.success? and name.present? - next if owner.projects.exists?(name: name) - description = `rpm -q --qf '[%{Description}]' -p #{srpm_file}`.scrub('') - - project = owner.projects.build( - name: name, - description: description, - visibility: visibility, - is_package: false # See: Hook for #attach_to_personal_repository - ) - project.owner = owner - if project.save - repository.projects << project rescue nil - project.update_attributes(is_package: true) - project.import_srpm srpm_file, platform.name - end - end - rescue => e - f.close if defined?(f) - Airbrake.notify_or_ignore(e, link: link.to_s, url: url, owner: owner) - ensure - File.delete srpm_file if srpm_file - end - end - rescue => e - Airbrake.notify_or_ignore(e, url: url, owner: owner) - ensure - FileUtils.remove_entry_secure dir if dir - end - - end - end - end -end diff --git a/lib/modules/models/markdown.rb b/lib/modules/models/markdown.rb deleted file mode 100644 index 4483fcb64..000000000 --- a/lib/modules/models/markdown.rb +++ /dev/null @@ -1,193 +0,0 @@ -# This module is based on -# https://github.com/gitlabhq/gitlabhq/blob/397c3da9758c03a215a308c011f94261d9c61cfa/lib/gitlab/markdown.rb -module Modules - module Models - # Custom parser for GitLab-flavored Markdown - # - # It replaces references in the text with links to the appropriate items in - # GitLab. - # - # Supported reference formats are: - # * @foo for team members - # * for issues & pull requests: - # * #123 - # * abf#123 - # * abf/rosa-build#123 - # * 123456 for commits - # - # It also parses Emoji codes to insert images. See - # http://www.emoji-cheat-sheet.com/ for a list of the supported icons. - # - # Examples - # - # >> gfm("Hey @david, can you fix this?") - # => "Hey @david, can you fix this?" - # - # >> gfm("Commit 35d5f7c closes #1234") - # => "Commit 35d5f7c closes #1234" - # - # >> gfm(":trollface:") - # => "\":trollface:\" - module Markdown - include IssuesHelper - - attr_reader :html_options - - # Public: Parse the provided text with GitLab-Flavored Markdown - # - # text - the source text - # html_options - extra options for the reference links as given to link_to - # - # Note: reference links will only be generated if @project is set - def gfm(text, html_options = {}) - return text if text.nil? - - # Duplicate the string so we don't alter the original, then call to_str - # to cast it back to a String instead of a SafeBuffer. This is required - # for gsub calls to work as we need them to. - text = text.dup.to_str - - @html_options = html_options - - # Extract pre blocks so they are not altered - # from http://github.github.com/github-flavored-markdown/ - text.gsub!(%r{
.*?
|.*?}m) { |match| extract_piece(match) } - # Extract links with probably parsable hrefs - text.gsub!(%r{.*?}m) { |match| extract_piece(match) } - # Extract images with probably parsable src - text.gsub!(%r{}m) { |match| extract_piece(match) } - - # TODO: add popups with additional information - - text = parse(text) - - # Insert pre block extractions - text.gsub!(/\{gfm-extraction-(\h{32})\}/) do - insert_piece($1) - end - - sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class) - end - - private - - def extract_piece(text) - @extractions ||= {} - - md5 = Digest::MD5.hexdigest(text) - @extractions[md5] = text - "{gfm-extraction-#{md5}}" - end - - def insert_piece(id) - @extractions[id] - end - - # Private: Parses text for references and emoji - # - # text - Text to parse - # - # Note: reference links will only be generated if @project is set - # - # Returns parsed text - def parse(text) - parse_references(text) if @project - parse_emoji(text) - - text - end - - REFERENCE_PATTERN = %r{ - (?[\W\/])? # Prefix - ( # Reference - @(?[a-zA-Z][a-zA-Z0-9_\-\.]*) # User/Group uname - |(?(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?\#[0-9]+) # Issue ID - |(?[\h]{6,40}) # Commit ID - ) - (?\W)? # Suffix - }x.freeze - - TYPES = [:user, :issue, :commit].freeze - - def parse_references(text) - # parse reference links - text.gsub!(REFERENCE_PATTERN) do |match| - prefix = $~[:prefix] - suffix = $~[:suffix] - type = TYPES.select{|t| !$~[t].nil?}.first - identifier = $~[type] - - # Avoid HTML entities - if prefix && suffix && prefix[0] == '&' && suffix[-1] == ';' - match - elsif ref_link = reference_link(type, identifier) - "#{prefix}#{ref_link}#{suffix}" - else - match - end - end - end - - EMOJI_PATTERN = %r{(:(\S+):)}.freeze - - def parse_emoji(text) - # parse emoji - text.gsub!(EMOJI_PATTERN) do |match| - if valid_emoji?($2) - image_tag(image_path("emoji/#{$2}.png"), class: 'emoji', title: $1, alt: $1, size: "20x20") - else - match - end - end - end - - # Private: Checks if an emoji icon exists in the image asset directory - # - # emoji - Identifier of the emoji as a string (e.g., "+1", "heart") - # - # Returns boolean - def valid_emoji?(emoji) - Emoji.names.include? emoji - end - - # Private: Dispatches to a dedicated processing method based on reference - # - # reference - Object reference ("@1234", "!567", etc.) - # identifier - Object identifier (Issue ID, SHA hash, etc.) - # - # Returns string rendered by the processing method - def reference_link(type, identifier) - send("reference_#{type}", identifier) - end - - def reference_user(identifier) - member = User.where(uname: identifier).first || Group.where(uname: identifier).first - if member - link_to("@#{identifier}", "/#{identifier}", html_options.merge(title: member.fullname, class: "gfm gfm-member #{html_options[:class]}")) - end - end - - def reference_issue(identifier) - if issue = Issue.find_by_hash_tag(identifier, current_ability, @project) - if issue.pull_request - title = "#{PullRequest.model_name.human}: #{issue.title}" - url = project_pull_request_path(issue.project, issue.pull_request) - else - title = "#{Issue.model_name.human}: #{issue.title}" - url = project_issue_path(issue.project.owner, issue.project.name, issue.serial_id) - end - link_to(identifier, url, html_options.merge(title: title, class: "gfm gfm-issue #{html_options[:class]}")) - end - end - - def reference_commit(identifier) - if commit = @project.repo.commit(identifier) - link_to shortest_hash_id(commit.id), commit_path(@project, commit.id) - title = GitPresenters::CommitAsMessagePresenter.present(commit, project: @project) do |presenter| - link_to(identifier, commit_path(@project, commit), html_options.merge(title: presenter.caption, class: "gfm gfm-commit #{html_options[:class]}")) - end - end - end - end - end -end diff --git a/lib/modules/models/owner.rb b/lib/modules/models/owner.rb deleted file mode 100644 index d9e8a6423..000000000 --- a/lib/modules/models/owner.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Modules - module Models - module Owner - extend ActiveSupport::Concern - - included do - validates :owner, presence: true - after_create -> { relations.create actor_id: owner.id, actor_type: owner.class.to_s, role: 'admin' } - end - - end - end -end diff --git a/lib/modules/models/personal_repository.rb b/lib/modules/models/personal_repository.rb deleted file mode 100644 index 2a4ba43fd..000000000 --- a/lib/modules/models/personal_repository.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Modules - module Models - module PersonalRepository - extend ActiveSupport::Concern - - included do - after_create :create_personal_repository, unless: :system? - end - - def create_personal_repository - begin - pl = own_platforms.build - pl.owner = self - pl.name = "#{self.uname}_personal" - pl.description = "#{self.uname}_personal" - pl.platform_type = 'personal' - pl.distrib_type = APP_CONFIG['distr_types'].first - pl.visibility = 'open' - pl.save! - - rep = pl.repositories.build - rep.name = 'main' - rep.description = 'main' - rep.save! - rescue Exception => e - pl.now_destroy rescue false - raise e - end - return true - end - - def personal_platform - own_platforms.personal.first - end - - def personal_repository - personal_platform.repositories.first - end - - module ClassMethods - end - end - end -end diff --git a/lib/modules/models/regeneration_status.rb b/lib/modules/models/regeneration_status.rb deleted file mode 100644 index 80e12341a..000000000 --- a/lib/modules/models/regeneration_status.rb +++ /dev/null @@ -1,48 +0,0 @@ -module Modules - module Models - module RegenerationStatus - extend ActiveSupport::Concern - - READY = 0 - WAITING_FOR_REGENERATION = 100 - REGENERATING = 200 - - HUMAN_STATUSES = { - READY => :ready, - WAITING_FOR_REGENERATION => :waiting_for_regeneration, - REGENERATING => :regenerating - } - - HUMAN_REGENERATION_STATUSES = { - AbfWorker::BaseObserver::COMPLETED => :completed, - AbfWorker::BaseObserver::FAILED => :failed, - AbfWorker::BaseObserver::CANCELED => :canceled - }.freeze - - included do - after_update :cleanup_file_store - - def sha1_of_file_store_files - files = [] - files << last_regenerated_log_sha1 if last_regenerated_log_sha1.present? - files - end - - def human_regeneration_status - self.class::HUMAN_REGENERATION_STATUSES[last_regenerated_status] || :no_data - end - - def human_status - self.class::HUMAN_STATUSES[status] || :no_data - end - - def cleanup_file_store - old_log_sha1 = last_regenerated_log_sha1_was - if old_log_sha1.present? && old_log_sha1 != last_regenerated_log_sha1 - later_destroy_files_from_file_store([old_log_sha1]) - end - end - end - end - end -end \ No newline at end of file diff --git a/lib/modules/models/time_living.rb b/lib/modules/models/time_living.rb deleted file mode 100644 index fde28e5e8..000000000 --- a/lib/modules/models/time_living.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Modules - module Models - module TimeLiving - extend ActiveSupport::Concern - - included do - - validates :time_living, numericality: { - only_integer: true - }, presence: true - - validate -> { - # MIN_TIME_LIVING <= time_living <= MAX_TIME_LIVING or - # 2 min <= time_living <= 12 hours - # time_living in seconds - min = self.class.const_defined?(:MIN_TIME_LIVING) ? self.class::MIN_TIME_LIVING : 120 - max = self.class.const_defined?(:MAX_TIME_LIVING) ? self.class::MAX_TIME_LIVING : 43200 - if min > time_living.to_i || time_living.to_i > max - errors.add :time_living, - I18n.t('flash.time_living.numericality_error', min: (min / 60), max: (max / 60)) - end - } - - before_validation :convert_time_living - attr_accessible :time_living - end - - protected - - def convert_time_living - self.time_living = time_living.to_i * 60 if time_living_was.to_i != time_living.to_i - end - end - end -end diff --git a/lib/modules/models/wiki.rb b/lib/modules/models/wiki.rb deleted file mode 100644 index 033df0881..000000000 --- a/lib/modules/models/wiki.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Modules - module Models - module Wiki - extend ActiveSupport::Concern - - included do - after_save :create_wiki - after_destroy :destroy_wiki - end - - def wiki_path - build_path(wiki_repo_name) - end - - def wiki_repo_name - File.join owner.uname, "#{name}.wiki" - end - - protected - - def create_wiki - if has_wiki && !FileTest.exist?(wiki_path) - Grit::Repo.init_bare(wiki_path) - wiki = Gollum::Wiki.new(wiki_path, {base_path: Rails.application.routes.url_helpers.project_wiki_index_path(owner, self)}) - wiki.write_page('Home', :markdown, I18n.t("wiki.seed.welcome_content"), - {name: owner.name, email: owner.email, message: 'Initial commit'}) - end - end - - def destroy_wiki - FileUtils.rm_rf wiki_path - end - - module ClassMethods - end - end - end -end