[#345] move lib/modules/models to app/models/concerns; small refactoring

This commit is contained in:
Alexander Machehin 2014-03-11 14:58:36 +06:00
parent a5f56bb835
commit 0a7d5d22da
40 changed files with 778 additions and 833 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -32,5 +32,4 @@ module BuildListObserver
end
end
end
end

View File

@ -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

View File

@ -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

View File

@ -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

241
app/models/concerns/git.rb Normal file
View File

@ -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

View File

@ -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 <a href="/users/david">@david</a>, can you fix this?"
#
# >> gfm("Commit 35d5f7c closes #1234")
# => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
#
# >> gfm(":trollface:")
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":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{<pre>.*?</pre>|<code>.*?</code>}m) { |match| extract_piece(match) }
# Extract links with probably parsable hrefs
text.gsub!(%r{<a.*?>.*?</a>}m) { |match| extract_piece(match) }
# Extract images with probably parsable src
text.gsub!(%r{<img.*?>}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{
(?<prefix>[\W\/])? # Prefix
( # Reference
@(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User/Group uname
|(?<issue>(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?\#[0-9]+) # Issue ID
|(?<commit>[\h]{6,40}) # Commit ID
)
(?<suffix>\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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,5 +1,5 @@
class ProjectTag < ActiveRecord::Base
include Modules::Models::FileStoreClean
include FileStoreClean
FORMATS = {
'zip' => 0,

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,6 @@
class User < Avatar
include Modules::Models::PersonalRepository
include Modules::Models::ActsLikeMember
include PersonalRepository
include ActsLikeMember
include Feed::User
include EventLoggable

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <a href="/users/david">@david</a>, can you fix this?"
#
# >> gfm("Commit 35d5f7c closes #1234")
# => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
#
# >> gfm(":trollface:")
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":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{<pre>.*?</pre>|<code>.*?</code>}m) { |match| extract_piece(match) }
# Extract links with probably parsable hrefs
text.gsub!(%r{<a.*?>.*?</a>}m) { |match| extract_piece(match) }
# Extract images with probably parsable src
text.gsub!(%r{<img.*?>}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{
(?<prefix>[\W\/])? # Prefix
( # Reference
@(?<user>[a-zA-Z][a-zA-Z0-9_\-\.]*) # User/Group uname
|(?<issue>(?:[a-zA-Z0-9\-_]*\/)?(?:[a-zA-Z0-9\-_]*)?\#[0-9]+) # Issue ID
|(?<commit>[\h]{6,40}) # Commit ID
)
(?<suffix>\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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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