diff --git a/Gemfile b/Gemfile index c1c2ad293..af5833308 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,6 @@ gem "russian" # gem 'ghoul_grack', '~> 0.0.1' gem 'grack', :git => 'git://github.com/rdblue/grack.git', :require => 'git_http' gem "grit" -gem 'gitolito', :git => 'git@github.com:warpc/gitolito.git' gem 'whenever', :require => false gem 'delayed_job' gem 'whenever', :require => false diff --git a/Gemfile.lock b/Gemfile.lock index 9a385acc5..e19ecdc96 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -18,13 +18,6 @@ GIT specs: grack (0.2.0) -GIT - remote: git@github.com:warpc/gitolito.git - revision: 120992e79d079800f6efb07f100ab80437b20b43 - specs: - gitolito (0.0.1) - grit - GEM remote: http://rubygems.org/ specs: @@ -286,7 +279,6 @@ DEPENDENCIES delayed_job devise (~> 1.4.8) factory_girl_rails (~> 1.3.0) - gitolito! grack! grit haml-rails (~> 0.3.4) diff --git a/app/controllers/auto_build_lists_controller.rb b/app/controllers/auto_build_lists_controller.rb index bdd6aa507..d9996c9b1 100644 --- a/app/controllers/auto_build_lists_controller.rb +++ b/app/controllers/auto_build_lists_controller.rb @@ -3,7 +3,7 @@ class AutoBuildListsController < ApplicationController before_filter :check_global_access def index - projects = Project.where(:owner_id => current_user.id, :owner_type => 'User') + projects = Project.where(:owner_id => current_user.id, :owner_type => 'User').order('name ASC') @projects_not_automated = projects.automateable.paginate(:page => params[:not_automated_page]) @projects_not_automated = @projects_not_automated.where(:name => params[:name]) unless params[:name].blank? diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 6b13c837c..af0df1015 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -60,25 +60,17 @@ class ProjectsController < ApplicationController redirect_to @project.owner end - def auto_build + # TODO remove this? + def auto_build uname, unixname = params[:git_repo].split('/') owner = User.find_by_uname(uname) || Group.find_by_uname(uname) project = Project.where(:owner_id => owner.id, :owner_type => owner.class).find_by_unixname!(unixname) - auto_build_list = AutoBuildList.find_by_project_id(project.id) + project.auto_build # p = params.delete_if{|k,v| k == 'controller' or k == 'action'} # ActiveSupport::Notifications.instrument("event_log.observer", :object => project, :message => p.inspect) logger.info "Git hook recieved from #{params[:git_user]} to #{params[:git_repo]}" - BuildList.create!( - :project => project, - :pl => auto_build_list.pl, - :bpl => auto_build_list.bpl, - :arch => auto_build_list.arch, - :project_version => project.collected_project_versions.last.try(:first), - :build_requires => true, - :update_type => 'bugfix') if auto_build_list - render :nothing => true end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 3e8ea9edb..085c310be 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -1,10 +1,9 @@ module ProjectsHelper - def git_repo_url(name) - port = APP_CONFIG['ssh_port'] || 22 - if port == 22 - "git@#{request.host}:#{name}.git" + def git_repo_url(name, read_only = true) + if current_user and !read_only + "http://#{current_user.uname}@#{request.host_with_port}/#{name}.git" else - "ssh://git@#{request.host}:#{port}/#{name}.git" + "http://#{request.host_with_port}/#{name}.git" end end end diff --git a/app/models/project.rb b/app/models/project.rb index 410546626..f0df85903 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -19,13 +19,12 @@ class Project < ActiveRecord::Base validates :name, :uniqueness => {:scope => [:owner_id, :owner_type]}, :presence => true, :allow_nil => false, :allow_blank => false validates :unixname, :uniqueness => {:scope => [:owner_id, :owner_type]}, :presence => true, :format => { :with => /^[a-z0-9_\-\+\.]+$/ }, :allow_nil => false, :allow_blank => false validates :owner, :presence => true - validate {errors.add(:base, I18n.t('flash.project.save_warning_ssh_key')) if owner.ssh_key.blank?} + # validate {errors.add(:base, I18n.t('flash.project.save_warning_ssh_key')) if owner.ssh_key.blank?} #attr_accessible :category_id, :name, :unixname, :description, :visibility attr_readonly :unixname scope :recent, order("name ASC") -# scope :by_name, lambda { |name| {:conditions => ['name like ?', '%' + name + '%']} } scope :by_name, lambda { |name| where('name like ?', '%' + name + '%') } scope :by_visibilities, lambda {|v| {:conditions => ['visibility in (?)', v.join(',')]}} scope :addable_to_repository, lambda { |repository_id| where("projects.id NOT IN (SELECT project_to_repositories.project_id FROM project_to_repositories WHERE (project_to_repositories.repository_id = #{ repository_id }))") } @@ -37,10 +36,21 @@ class Project < ActiveRecord::Base after_create :attach_to_personal_repository after_create :create_git_repo - before_update :update_git_repo after_destroy :destroy_git_repo after_rollback lambda { destroy_git_repo rescue true if new_record? } + def auto_build + auto_build_lists.each do |auto_build_list| + build_lists.create( + :pl => auto_build_list.pl, + :bpl => auto_build_list.bpl, + :arch => auto_build_list.arch, + :project_version => collected_project_versions.last.try(:first), + :build_requires => true, + :update_type => 'bugfix') + end + end + def project_versions res = tags.select { |tag| tag.name =~ /^v\./ } return res if res and res.size > 0 @@ -56,7 +66,7 @@ class Project < ActiveRecord::Base end def members - collaborators + groups + collaborators + groups.map(&:members).flatten end # include Project::HasRepository @@ -70,12 +80,8 @@ class Project < ActiveRecord::Base @git_repo_path ||= path end def git_repo_name - [owner.uname, unixname].join('/') + File.join owner.uname, unixname end - def git_repo_name_was - [owner.uname, unixname_was].join('/') - end - def git_repo_name_changed?; git_repo_name != git_repo_name_was; end def public? visibility == 'open' @@ -130,29 +136,11 @@ class Project < ActiveRecord::Base end def create_git_repo - with_ga do |ga| - repo = ga.add_repo git_repo_name - repo.add_key owner.ssh_key, 'RW' - repo.has_anonymous_access!('R') if public? - ga.save_and_release - end - end - - def update_git_repo - with_ga do |ga| - if repo = ga.find_repo(git_repo_name_was) - repo.rename(git_repo_name) if git_repo_name_changed? - public? ? repo.has_anonymous_access!('R') : repo.has_not_anonymous_access! - ga.save_and_release - end - end if git_repo_name_changed? or visibility_changed? + Grit::Repo.init_bare git_repo_path end def destroy_git_repo - with_ga do |ga| - ga.rm_repo git_repo_name - ga.save_and_release - end + FileUtils.rm_rf git_repo_path end def add_owner_rel diff --git a/app/models/relation.rb b/app/models/relation.rb index f8d4a35b2..81f9a8f1e 100644 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@ -5,23 +5,6 @@ class Relation < ActiveRecord::Base has_many :role_lines has_many :roles, :autosave => true, :through => :role_lines - after_create { - with_ga do |ga| - if repo = ga.find_repo(target.git_repo_name) and key = object.ssh_key and key.present? - repo.add_key(key, 'RW', :force => true) - ga.save_and_release - end - end if target_type == 'Project' and object_type == 'User' - } - after_destroy { - with_ga do |ga| - if repo = ga.find_repo(target.git_repo_name) and key = object.ssh_key and key.present? - repo.rm_key(key) - ga.save_and_release - end - end if target_type == 'Project' and object_type == 'User' - } - scope :by_object, lambda {|obj| {:conditions => ['object_id = ? AND object_type = ?', obj.id, obj.class.to_s]}} scope :by_target, lambda {|tar| {:conditions => ['target_id = ? AND target_type = ?', tar.id, tar.class.to_s]}} end diff --git a/app/models/user.rb b/app/models/user.rb index b7d94f530..9a32e4e0f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -27,27 +27,12 @@ class User < ActiveRecord::Base validates :uname, :presence => true, :uniqueness => {:case_sensitive => false}, :format => { :with => /^[a-z0-9_]+$/ }, :allow_nil => false, :allow_blank => false validates :ssh_key, :uniqueness => true, :allow_blank => true validate { errors.add(:uname, :taken) if Group.where('uname LIKE ?', uname).present? } - #TODO: Replace this simple cross-table uniq validation by more progressive analog - validate lambda { - errors.add(:uname, I18n.t('flash.user.group_uname_exists')) if Group.exists? :uname => uname - } attr_accessible :email, :password, :password_confirmation, :remember_me, :login, :name, :ssh_key, :uname attr_readonly :uname attr_accessor :login before_create :add_default_role - - before_update { - if ssh_key_was.blank? and ssh_key.present? - create_ssh_key ssh_key - elsif ssh_key_was.present? and ssh_key.blank? - destroy_ssh_key ssh_key_was - elsif ssh_key_changed? and ssh_key.present? and ssh_key_was.present? - update_ssh_key ssh_key_was, ssh_key - end - } - before_destroy { destroy_ssh_key(ssh_key) if ssh_key.present? } # after_create() { UserMailer.new_user_notification(self).deliver } class << self @@ -83,33 +68,4 @@ class User < ActiveRecord::Base clean_up_passwords result end - - protected - - def create_ssh_key(key) - with_ga do |ga| - ga.store_key! key - projects.each do |project| - repo = ga.find_repo(project.git_repo_name) - repo.add_key(key, 'RW') if repo - end - ga.save_and_release - end - end - - def update_ssh_key(old_key, new_key) - with_ga do |ga| - ga.repos.replace_key old_key, new_key #, options = {} - ga.replace_key! old_key, new_key - ga.save_and_release - end - end - - def destroy_ssh_key(key) - with_ga do |ga| - ga.repos.rm_key key - ga.rm_key! key - ga.save_and_release - end - end end diff --git a/app/views/auto_build_lists/_already_automated.haml b/app/views/auto_build_lists/_already_automated.haml index 22c565794..49ade241f 100644 --- a/app/views/auto_build_lists/_already_automated.haml +++ b/app/views/auto_build_lists/_already_automated.haml @@ -10,4 +10,4 @@ %td= link_to project.name, project_path(project) %td= link_to t("layout.auto_build_lists.cancel_btn"), auto_build_list_path(project.auto_build_lists_id), :method => :delete, :confirm => t("layout.confirm") -= will_paginate @projects_not_automated, :param_name => 'already_automated_page' \ No newline at end of file += will_paginate @projects_already_automated, :param_name => 'already_automated_page' \ No newline at end of file diff --git a/config/initializers/load_config.rb b/config/initializers/load_config.rb index af181a870..b9a801033 100644 --- a/config/initializers/load_config.rb +++ b/config/initializers/load_config.rb @@ -6,3 +6,5 @@ config = {:project_root => File.join(APP_CONFIG['root_path'], 'git_projects'), : # Rosa::Application.config.middleware.insert_before ::ActionDispatch::Static, ::Grack::Handler, config Rosa::Application.config.middleware.insert_after ::Rails::Rack::Logger, ::Grack::Handler, config Rosa::Application.config.middleware.insert_before ::Grack::Handler, ::Grack::Auth + +# Grit::Git.git_timeout = 60 diff --git a/config/locales/ru.yml b/config/locales/ru.yml index ceea58eea..4aa51e29f 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -338,7 +338,6 @@ ru: saved: Пользователь успешно сохранен save_error: Не удалось сохранить данные о пользователе destroyed: Учетная запись успешно удалена - group_uname_exists: Группа с таким именем уже зарегестрирована group: saved: Группа успешно сохранена diff --git a/lib/ext/active_support/object.rb b/lib/ext/active_support/object.rb deleted file mode 100644 index ba2948685..000000000 --- a/lib/ext/active_support/object.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'gitolito' - -Grit::Git.git_timeout = 60 -# Grit::Git.git_max_size = 20.megabytes - -class Object - def with_ga(&block) - ::Gitolito::GitoliteAdmin.thread_safe(File.join(APP_CONFIG['root_path'], 'gitolite-admin'), {:wait_lock => true, :seconds => 60}) do |ga| - block.call(ga) - end - # ga = Gitolito::GitoliteAdmin.new File.join(APP_CONFIG['root_path'], 'gitolite-admin'); block.call(ga) - end -end diff --git a/lib/grack/auth.rb b/lib/grack/auth.rb index 07feae63f..a7809292e 100644 --- a/lib/grack/auth.rb +++ b/lib/grack/auth.rb @@ -4,28 +4,19 @@ module Grack @app = app end - def call(env) - if git?(env) - uname, unixname = env['PATH_INFO'].split('/')[1,2] - unixname.gsub! /\.git$/, '' - owner = User.find_by_uname(uname) || Group.find_by_uname(uname) - project = Project.where(:owner_id => owner.id, :owner_type => owner.class).find_by_unixname(unixname) - return [404, {}, []] if project.blank? + # TODO tests!!! + def call(env) + super + if git? + return [404, {"Content-Type" => "text/plain"}, ["Not Found"]] if project.blank? - # TODO r/rw ? - unless project.public? # need auth - ::Rack::Auth::Basic.new(@app) do |user, password| - user = User.find_for_database_authentication(:login => user) and user.valid_password?(password) and - users = project.collaborators << project.groups.map(&:members).flatten and users.include?(user) # TODO ACL ? - # env['REQUEST_METHOD'] == 'GET' # read - # env['REQUEST_METHOD'] == 'POST' # write - end.call(env) - else - @app.call(env) - end - else - @app.call(env) + return ::Rack::Auth::Basic.new(@app) do |u, p| + user = User.find_for_database_authentication(:login => u) and user.valid_password?(p) and + project.members.include?(user) # TODO ACL + # ability = ::Ability.new(user) and ability.can?(action, project) + end.call(env) unless project.public? and read? # need auth end + @app.call(env) # next app in stack end end end diff --git a/lib/grack/base.rb b/lib/grack/base.rb index 4b3c1a935..e5290572e 100644 --- a/lib/grack/base.rb +++ b/lib/grack/base.rb @@ -1,11 +1,36 @@ module Grack class Base # abstract - def git?(env) - env['HTTP_USER_AGENT'] =~ /git\// + def call(env) + @env = env + @project = nil + end + + def git? + @env['HTTP_USER_AGENT'] =~ /^git\// + end + + def read? + @env['REQUEST_URI'] =~ /git-upload-pack$/ + end + + def write? + @env['REQUEST_URI'] =~ /git-receive-pack$/ + end + + def action + write? ? 'update' : 'read' + end + + def project + @project ||= begin + uname, unixname = @env['PATH_INFO'].split('/')[1,2] + unixname.gsub! /\.git$/, '' + owner = User.find_by_uname(uname) || Group.find_by_uname(uname) + Project.where(:owner_id => owner.id, :owner_type => owner.class).find_by_unixname(unixname) + end end end end # ({"HTTP_ACCEPT"=>"*/*", "HTTP_HOST"=>"localhost:3000", "SERVER_NAME"=>"localhost", "rack.url_scheme"=>"http", "PASSENGER_CONNECT_PASSWORD"=>"xbRC6murG5bIDTsaed8ksaZhjf8yFsadlX4QL0qWNbS", "HTTP_USER_AGENT"=>"git/1.7.7.2", "PASSENGER_SPAWN_METHOD"=>"smart-lv2", "PASSENGER_FRIENDLY_ERROR_PAGES"=>"true", "CONTENT_LENGTH"=>"0", "rack.errors"=>#, "SERVER_PROTOCOL"=>"HTTP/1.1", "action_dispatch.secret_token"=>"df2fb72d477491cf15ef0f93449bcb59c3412c255c2386e07772935565c1b6ad23539ed804b8f12e3221e47abb78f5b679693c391acb33477be0e633e7a2e2a4", "rack.run_once"=>false, "rack.version"=>[1, 0], "REMOTE_ADDR"=>"127.0.0.1", "SERVER_SOFTWARE"=>"nginx/1.0.6", "PASSENGER_MIN_INSTANCES"=>"1", "PATH_INFO"=>"/codefoundry.git/info/refs", "SERVER_ADDR"=>"127.0.0.1", "SCRIPT_NAME"=>"", "action_dispatch.parameter_filter"=>[:password], "action_dispatch.show_exceptions"=>true, "rack.multithread"=>false, "PASSENGER_USER"=>"", "PASSENGER_ENVIRONMENT"=>"development", "PASSENGER_SHOW_VERSION_IN_HEADER"=>"true", "rack.multiprocess"=>true, "REMOTE_PORT"=>"49387", "REQUEST_URI"=>"/codefoundry.git/info/refs", "SERVER_PORT"=>"3000", "SCGI"=>"1", "PASSENGER_APP_TYPE"=>"rack", "PASSENGER_USE_GLOBAL_QUEUE"=>"true", "REQUEST_METHOD"=>"GET", "PASSENGER_GROUP"=>"", "PASSENGER_DEBUGGER"=>"false", "DOCUMENT_ROOT"=>"/Users/pasha/Sites/rosa-build/public", "_"=>"_", "PASSENGER_FRAMEWORK_SPAWNER_IDLE_TIME"=>"-1", "UNION_STATION_SUPPORT"=>"false", "rack.input"=>#>, @unlinked=false>, "HTTP_PRAGMA"=>"no-cache", "QUERY_STRING"=>"", "PASSENGER_APP_SPAWNER_IDLE_TIME"=>"-1"}) (process 41940, thread #) # {"rack.session"=>{}, "HTTP_ACCEPT"=>"*/*", "HTTP_HOST"=>"localhost:3000", "SERVER_NAME"=>"localhost", "action_dispatch.remote_ip"=>#, "rack.url_scheme"=>"http", "PASSENGER_CONNECT_PASSWORD"=>"dljpLA91qGH4v2gwaccoAxFysOmSkEFbRtPyPOe9953", "HTTP_USER_AGENT"=>"git/1.7.7.2", "PASSENGER_SPAWN_METHOD"=>"smart-lv2", "PASSENGER_FRIENDLY_ERROR_PAGES"=>"true", "CONTENT_LENGTH"=>"0", "action_dispatch.request.unsigned_session_cookie"=>{}, "rack.errors"=>#, "SERVER_PROTOCOL"=>"HTTP/1.1", "action_dispatch.secret_token"=>"df2fb72d477491cf15ef0f93449bcb59c3412c255c2386e07772935565c1b6ad23539ed804b8f12e3221e47abb78f5b679693c391acb33477be0e633e7a2e2a4", "rack.run_once"=>false, "rack.version"=>[1, 0], "REMOTE_ADDR"=>"127.0.0.1", "SERVER_SOFTWARE"=>"nginx/1.0.6", "PASSENGER_MIN_INSTANCES"=>"1", "PATH_INFO"=>"/pasha/mc.git/info/refs", "SERVER_ADDR"=>"127.0.0.1", "SCRIPT_NAME"=>"", "action_dispatch.parameter_filter"=>[:password], "action_dispatch.show_exceptions"=>true, "rack.multithread"=>false, "PASSENGER_USER"=>"", "PASSENGER_ENVIRONMENT"=>"development", "PASSENGER_SHOW_VERSION_IN_HEADER"=>"true", "action_dispatch.cookies"=>{}, "rack.multiprocess"=>true, "REMOTE_PORT"=>"49643", "REQUEST_URI"=>"/pasha/mc.git/info/refs", "SERVER_PORT"=>"3000", "SCGI"=>"1", "PASSENGER_APP_TYPE"=>"rack", "PASSENGER_USE_GLOBAL_QUEUE"=>"true", "rack.session.options"=>{:httponly=>true, :expire_after=>nil, :domain=>nil, :path=>"/", :secure=>false, :id=>nil}, "REQUEST_METHOD"=>"GET", "PASSENGER_GROUP"=>"", "PASSENGER_DEBUGGER"=>"false", "DOCUMENT_ROOT"=>"/Users/pasha/Sites/rosa-build/public", "warden"=>Warden::Proxy:2242130160 @config={:failure_app=>Devise::FailureApp, :default_scope=>:user, :intercept_401=>false, :scope_defaults=>{}, :default_strategies=>{:user=>[:rememberable, :database_authenticatable]}}, "_"=>"_", "PASSENGER_FRAMEWORK_SPAWNER_IDLE_TIME"=>"-1", "UNION_STATION_SUPPORT"=>"false", "rack.input"=>#>, @unlinked=false>, "HTTP_PRAGMA"=>"no-cache", "QUERY_STRING"=>"", "PASSENGER_APP_SPAWNER_IDLE_TIME"=>"-1"} -#, @check_after=1321397135.42431, @dwell=1.0>, @config={:failure_app=>Devise::FailureApp, :intercept_401=>false, :default_scope=>:user, :scope_defaults=>{}, :default_strategies=>{:user=>[:rememberable, :database_authenticatable]}}>, @header="IE=Edge">>>, @parsers={#=>:xml_simple, #=>:json}>>, @default_options={:expire_after=>nil, :httponly=>true, :path=>"/", :domain=>nil, :secure=>false}, @key="_rosa_session", @cookie_only=true>>>>, @prepare_each_request=true>>, @trusted_proxies=/(^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.)/i>, @consider_all_requests_local=true>, @config={:receive_pack=>true, :git_path=>"/opt/local/bin/git", :upload_pack=>true, :project_root=>"/var/rosa/git_projects"}> diff --git a/lib/grack/handler.rb b/lib/grack/handler.rb index 0816deacf..9f8833f5f 100644 --- a/lib/grack/handler.rb +++ b/lib/grack/handler.rb @@ -6,7 +6,10 @@ module Grack end def call(env) - if git?(env) + super + if git? + # TODO event_log? called twice! + project.auto_build if write? # hook ::GitHttp::App.new(@config).call(env) else @app.call(env) diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index 51b26e19b..375bc145d 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -1,15 +1,15 @@ namespace :import do desc "Load projects" task :projects => :environment do + # open('http://dl.dropbox.com/u/984976/sorted4.txt').readlines.each do |name| # TODO clean names open('http://dl.dropbox.com/u/984976/PackageList.txt').readlines.each do |name| - name.chomp! + name.chomp!; name.downcase! print "Import #{name}..." - owner = User.find(2) # vsharshov@gmail.com + owner = User.find(1) # I am # owner = Group.find(1) # Core Team # puts Project.create(:name => name, :unixname => name) {|p| p.owner = owner} ? "Ok!" : "Fail" p = Project.find_or_create_by_name_and_unixname(name, name) {|p| p.owner = owner} puts p.persisted? ? "Ok!" : "Fail!" - # sleep 1 end puts 'DONE' end