├── .gitignore ├── app ├── helpers │ ├── repository_mirrors_helper.rb │ ├── sys_helper.rb │ ├── users_helper.rb │ ├── gitolite_public_keys_helper.rb │ ├── application_helper.rb │ └── git_hosting_helper.rb ├── models │ ├── git_cache.rb │ ├── git_cia_notification.rb │ ├── repository_post_receive_url.rb │ ├── git_repository_extra.rb │ ├── deployment_credential.rb │ ├── cia_notification_mailer.rb │ ├── git_hosting_observer.rb │ └── repository_mirror.rb ├── views │ ├── repository_mirrors │ │ ├── form_error.rjs │ │ ├── edit.html.erb │ │ ├── create.html.erb │ │ ├── destroy.html.erb │ │ ├── push.html.erb │ │ ├── _form.html.erb │ │ └── _view_list.html.erb │ ├── repository_post_receive_urls │ │ ├── form_error.rjs │ │ ├── _form.html.erb │ │ ├── edit.html.erb │ │ ├── create.html.erb │ │ ├── destroy.html.erb │ │ └── _view_list.html.erb │ ├── deployment_credentials │ │ ├── form_error.rjs │ │ ├── create_with_key.html.erb │ │ ├── edit.html.erb │ │ ├── destroy.html.erb │ │ ├── _form_with_key.html.erb │ │ └── _view_list.html.erb │ ├── gitolite_public_keys │ │ ├── form_error.rjs │ │ └── _view.html.erb │ ├── repositories │ │ ├── edit.html.erb │ │ ├── git_instructions.html.erb │ │ ├── _git_urls.erb │ │ └── _form.html.erb │ ├── cia_notification_mailer │ │ └── cia_notification.erb │ ├── layouts │ │ └── popup.erb │ ├── settings │ │ └── _display_access.html.erb │ ├── my │ │ └── account.html.erb │ └── projects │ │ ├── _git_urls.erb │ │ └── settings │ │ └── _repository.html.erb └── controllers │ ├── repository_post_receive_urls_controller.rb │ ├── repository_mirrors_controller.rb │ └── gitolite_public_keys_controller.rb ├── selinux ├── redmine_git.pp ├── redmine_git.fc ├── redmine_git.sh ├── redmine_git.te ├── README └── redmine_git.if ├── assets ├── images │ ├── paste.png │ ├── icon-divide.png │ ├── ZeroClipboard.swf │ ├── button.svg │ ├── button_selected.svg │ └── button_focus.svg ├── stylesheets │ ├── modalbox │ │ ├── spinner.gif │ │ └── modalbox.css │ ├── application.css │ ├── zero_clipboard.css │ └── git_url_display.css └── javascripts │ ├── notify_cia_test.js │ ├── zero_clipboard_setup.js │ ├── git_url_display.js │ └── modalbox │ └── lib │ └── scriptaculous.js ├── Gemfile ├── Rakefile ├── .loadpath ├── test ├── test_helper.rb ├── fixtures │ └── gitolite_public_keys.yml └── unit │ └── gitolite_public_key_test.rb ├── db └── migrate │ ├── 2012052200000_add_post_receive_url_modes.rb │ ├── 20111119170948_add_indexes_to_gitolite_public_key.rb │ ├── 20091119162428_create_git_caches.rb │ ├── 2011080700000_create_repository_mirrors.rb │ ├── 2012052100000_create_repository_post_receive_urls.rb │ ├── 20091119162427_create_gitolite_public_keys.rb │ ├── 2011072600000_extend_changesets_notified_cia.rb │ ├── 20120710204007_add_repository_mirror_fields.rb │ ├── 20120708070841_add_settings_to_plugin_4.rb │ ├── 20120724211806_add_settings_to_plugin_5.rb │ ├── 20091119162426_set_mirror_role_permissions.rb │ ├── 2012052100001_set_post_receive_url_role_permissions.rb │ ├── 20111123214911_add_settings_to_plugin.rb │ ├── 2011081700000_move_notified_cia_to_git_cia_notifications.rb │ ├── 20120226013750_add_settings_to_plugin_3.rb │ ├── 20120803043256_create_deployment_credentials.rb │ ├── 20111220055819_add_settings_to_plugin_2.rb │ ├── 2011081300000_create_git_repository_extras.rb │ └── 20120904060609_update_multi_repo_per_project.rb ├── lib ├── git_hosting │ └── patches │ │ ├── user_patch.rb │ │ ├── sys_controller_patch.rb │ │ ├── groups_controller_patch.rb │ │ ├── repository_cia_filters.rb │ │ ├── my_controller_patch.rb │ │ ├── git_repository_patch.rb │ │ ├── project_patch.rb │ │ ├── roles_controller_patch.rb │ │ ├── git_adapter_patch.rb │ │ ├── users_controller_patch.rb │ │ ├── members_controller_patch.rb │ │ ├── repositories_controller_patch.rb │ │ └── projects_controller_patch.rb └── gitolite_recycle.rb ├── tasks ├── gitolite.rake └── redmine_git_hosting.rake ├── config └── routes.rb ├── contrib └── hooks │ └── post-receive.redmine_gitolite.rb └── init.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | bin/ 3 | selinux/tmp/ 4 | -------------------------------------------------------------------------------- /app/helpers/repository_mirrors_helper.rb: -------------------------------------------------------------------------------- 1 | module RepositoryMirrorsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sys_helper.rb: -------------------------------------------------------------------------------- 1 | # Added blank help for Rails 1.9.3+ 2 | module SysHelper 3 | end 4 | -------------------------------------------------------------------------------- /selinux/redmine_git.pp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubitron/redmine_git_hosting/HEAD/selinux/redmine_git.pp -------------------------------------------------------------------------------- /assets/images/paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubitron/redmine_git_hosting/HEAD/assets/images/paste.png -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | gem "inifile", "~>0.4.1" 3 | gem "lockfile", "~>1.4.3" 4 | gem "net-ssh", "~>2.1.4" 5 | -------------------------------------------------------------------------------- /assets/images/icon-divide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubitron/redmine_git_hosting/HEAD/assets/images/icon-divide.png -------------------------------------------------------------------------------- /assets/images/ZeroClipboard.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubitron/redmine_git_hosting/HEAD/assets/images/ZeroClipboard.swf -------------------------------------------------------------------------------- /app/models/git_cache.rb: -------------------------------------------------------------------------------- 1 | class GitCache < ActiveRecord::Base 2 | attr_accessible :command, :command_output, :repo_identifier 3 | end 4 | -------------------------------------------------------------------------------- /assets/stylesheets/modalbox/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kubitron/redmine_git_hosting/HEAD/assets/stylesheets/modalbox/spinner.gif -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'tmpdir' 3 | require 'lockfile' 4 | require 'net/ssh' 5 | rescue 6 | puts "need to install tmpdir lockfile and net/ssh gems" 7 | end 8 | -------------------------------------------------------------------------------- /.loadpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/views/repository_mirrors/form_error.rjs: -------------------------------------------------------------------------------- 1 | page.replace_html 'validation_messages', (error_messages_for 'mirror') 2 | page << "if (typeof(Modalbox) != 'undefined' ) { Modalbox.resizeToContent(); }" 3 | 4 | -------------------------------------------------------------------------------- /app/views/repository_post_receive_urls/form_error.rjs: -------------------------------------------------------------------------------- 1 | page.replace_html 'validation_messages', (error_messages_for 'prurl') 2 | page << "if (typeof(Modalbox) != 'undefined' ) { Modalbox.resizeToContent(); }" 3 | 4 | -------------------------------------------------------------------------------- /app/views/deployment_credentials/form_error.rjs: -------------------------------------------------------------------------------- 1 | page.replace_html 'validation_messages', ((error_messages_for 'cred')+(error_messages_for 'key')) 2 | page << "if (typeof(Modalbox) != 'undefined' ) { Modalbox.resizeToContent(); }" 3 | 4 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Load the normal Rails helper 2 | require File.expand_path(File.dirname(__FILE__) + '/../../../../test/test_helper') 3 | 4 | # Ensure that we are using the temporary fixture path 5 | Engines::Testing.set_fixture_path 6 | -------------------------------------------------------------------------------- /test/fixtures/gitolite_public_keys.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html 2 | one: 3 | id: 1 4 | title: MyString 5 | key: MyText 6 | two: 7 | id: 2 8 | title: MyString 9 | key: MyText 10 | -------------------------------------------------------------------------------- /app/views/gitolite_public_keys/form_error.rjs: -------------------------------------------------------------------------------- 1 | page.replace_html 'validation_messages', (error_messages_for 'gitolite_public_key') 2 | page.select('div.notice').each do |d| 3 | page.replace d, nil 4 | end 5 | page[:cancel_button_div].show 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/models/git_cia_notification.rb: -------------------------------------------------------------------------------- 1 | class GitCiaNotification < ActiveRecord::Base 2 | 3 | belongs_to :repository 4 | 5 | validates_uniqueness_of :scmid, :scope => [:repository_id] 6 | validates_presence_of :repository_id, :scmid 7 | 8 | validates_associated :repository 9 | 10 | end 11 | -------------------------------------------------------------------------------- /test/unit/gitolite_public_key_test.rb: -------------------------------------------------------------------------------- 1 | require File.dirname(__FILE__) + '/../test_helper' 2 | 3 | class GitolitePublicKeyTest < ActiveSupport::TestCase 4 | fixtures :gitolite_public_keys 5 | 6 | # Replace this with your real tests. 7 | def test_truth 8 | assert true 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/2012052200000_add_post_receive_url_modes.rb: -------------------------------------------------------------------------------- 1 | class AddPostReceiveUrlModes < ActiveRecord::Migration 2 | def self.up 3 | add_column :repository_post_receive_urls, :mode, :string, :default => "github" 4 | end 5 | 6 | def self.down 7 | remove_column :repository_post_receive_urls, :mode 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20111119170948_add_indexes_to_gitolite_public_key.rb: -------------------------------------------------------------------------------- 1 | class AddIndexesToGitolitePublicKey < ActiveRecord::Migration 2 | def self.up 3 | add_index :gitolite_public_keys, :user_id 4 | add_index :gitolite_public_keys, :identifier 5 | end 6 | 7 | def self.down 8 | remove_index :gitolite_public_keys, :user_id 9 | remove_index :gitolite_public_keys, :identifier 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20091119162428_create_git_caches.rb: -------------------------------------------------------------------------------- 1 | class CreateGitCaches < ActiveRecord::Migration 2 | def self.up 3 | create_table :git_caches do |t| 4 | t.column :command, :text 5 | t.column :command_output, :binary 6 | t.column :proj_identifier, :string 7 | t.timestamps 8 | t.index :command 9 | end 10 | end 11 | 12 | def self.down 13 | drop_table :git_caches 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/repository_mirrors/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_mirror_edit)%>

<% end %> 2 | <% labelled_remote_form_for :repository_mirrors, @mirror, 3 | :url => { :controller => 'repository_mirrors', :action => 'update', :project_id => @project, :id => @mirror.id }, 4 | :html => {:method => :post, :class => 'tabular'} do |f| %> 5 | <%= render :partial => 'form', :locals => { :f => f } %> 6 | <%= submit_tag l(:button_save) %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/repository_mirrors/create.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_mirror_create)%>

<% end %> 2 | <% labelled_remote_form_for :repository_mirrors, @mirror, 3 | :url => { :controller => 'repository_mirrors', :action => 'create', :project_id => @project, :id => @mirror.id }, 4 | :html => {:method => :post, :class => 'tabular'} do |f| %> 5 | <%= render :partial => 'form', :locals => { :f => f } %> 6 | <%= submit_tag l(:button_save) %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /db/migrate/2011080700000_create_repository_mirrors.rb: -------------------------------------------------------------------------------- 1 | class CreateRepositoryMirrors < ActiveRecord::Migration 2 | def self.up 3 | create_table :repository_mirrors do |t| 4 | t.column :project_id, :integer 5 | t.column :active, :integer, :default => 1 6 | t.column :url, :string 7 | t.references :project 8 | t.timestamps 9 | end 10 | end 11 | 12 | def self.down 13 | drop_table :repository_mirrors 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/repository_post_receive_urls/_form.html.erb: -------------------------------------------------------------------------------- 1 |
<%= error_messages_for 'prurl' %>
2 | 3 |
4 | 5 |

<%= f.text_field :url, :required => true, :size => 65 %>

6 |

<%= f.select :mode, [['Github-style POST', :github], ['Empty GET request', :get]] %>

7 |

<%= f.check_box :active %>

8 |
9 | 10 | -------------------------------------------------------------------------------- /selinux/redmine_git.fc: -------------------------------------------------------------------------------- 1 | # We do not install contexts as part of the policy, since it is likely 2 | # to be overridden by the local policy for parent directories. The rakefile 3 | # ( ../tasks/selinux.rake) will install file contexts "manually". 4 | # 5 | # If you really want to install a file context, uncomment the following 6 | # and rerun the install script or rake task 7 | # 8 | # /.*/redmine/vendor/plugins/redmine_git_hosting/bin(/.*)? httpd_redmine_git_script_exec_t 9 | 10 | -------------------------------------------------------------------------------- /app/views/deployment_credentials/create_with_key.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_deployment_credential_create)%>

<% end %> 2 | <% labelled_remote_form_for :deployment_credentials, @cred, 3 | :url => { :controller => 'deployment_credentials', :action => 'create_with_key', :project_id => @project, :id => @cred.id }, 4 | :html => {:method => :post} do |f| %> 5 | <%= render :partial => 'form_with_key', :locals => { :f => f } %> 6 | <%= submit_tag l(:button_save) %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/repository_post_receive_urls/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_post_receive_url_edit)%>

<% end %> 2 | <% labelled_remote_form_for :repository_post_receive_urls, @prurl, 3 | :url => { :controller => 'repository_post_receive_urls', :action => 'update', :project_id => @project, :id => @prurl.id }, 4 | :html => {:method => :post, :class => 'tabular'} do |f| %> 5 | <%= render :partial => 'form', :locals => { :f => f } %> 6 | <%= submit_tag l(:button_save) %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /db/migrate/2012052100000_create_repository_post_receive_urls.rb: -------------------------------------------------------------------------------- 1 | class CreateRepositoryPostReceiveUrls < ActiveRecord::Migration 2 | def self.up 3 | create_table :repository_post_receive_urls do |t| 4 | t.column :project_id, :integer 5 | t.column :active, :integer, :default => 1 6 | t.column :url, :string 7 | t.references :project 8 | t.timestamps 9 | end 10 | end 11 | 12 | def self.down 13 | drop_table :repository_post_receive_urls 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/repository_post_receive_urls/create.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_post_receive_url_create)%>

<% end %> 2 | <% labelled_remote_form_for :repository_post_receive_urls, @prurl, 3 | :url => { :controller => 'repository_post_receive_urls', :action => 'create', :project_id => @project, :id => @prurl.id }, 4 | :html => {:method => :post, :class => 'tabular'} do |f| %> 5 | <%= render :partial => 'form', :locals => { :f => f } %> 6 | <%= submit_tag l(:button_save) %> 7 | <% end %> 8 | -------------------------------------------------------------------------------- /db/migrate/20091119162427_create_gitolite_public_keys.rb: -------------------------------------------------------------------------------- 1 | class CreateGitolitePublicKeys < ActiveRecord::Migration 2 | def self.up 3 | create_table :gitolite_public_keys do |t| 4 | t.column :title, :string 5 | t.column :identifier, :string 6 | t.column :key, :text 7 | t.column :active, :integer, :default => 1 8 | t.references :user 9 | t.timestamps 10 | end 11 | 12 | end 13 | 14 | def self.down 15 | drop_table :gitolite_public_keys 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/user_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | 5 | module GitHosting 6 | module Patches 7 | module UserPatch 8 | def self.included(base) 9 | base.class_eval do 10 | unloadable 11 | 12 | # initialize association from user -> gitolite_public_keys 13 | has_many :gitolite_public_keys, :dependent => :destroy 14 | end 15 | end 16 | end 17 | end 18 | end 19 | 20 | # Patch in changes 21 | User.send(:include, GitHosting::Patches::UserPatch) 22 | -------------------------------------------------------------------------------- /db/migrate/2011072600000_extend_changesets_notified_cia.rb: -------------------------------------------------------------------------------- 1 | class ExtendChangesetsNotifiedCia < ActiveRecord::Migration 2 | def self.up 3 | add_column :changesets, :notified_cia, :integer, :default=>0 4 | end 5 | 6 | def self.down 7 | # Deal with fact that one of next migrations doesn't restore :notified_cia 8 | remove_column :changesets, :notified_cia if self.column_exists?(:changesets, :notified_cia) 9 | end 10 | 11 | def self.column_exists?(table_name, column_name) 12 | columns(table_name).any?{ |c| c.name == column_name.to_s } 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /assets/images/button.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/views/repository_mirrors/destroy.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_mirror_delete)%>

<% end %> 2 |
3 |

4 | <%= @mirror.url %>
5 | Are you sure you want to delete the repository mirror? 6 |

7 | <% form_tag url_for(:controller => 'repository_mirrors', :action => 'destroy', :project_id => @project, :id => @mirror.id ), :method => :delete do %> 8 |

9 | 10 | <%= submit_tag l(:button_delete) %> 11 |

12 | <% end %> 13 |
14 | -------------------------------------------------------------------------------- /assets/images/button_selected.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/images/button_focus.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/views/repository_post_receive_urls/destroy.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_post_receive_url_delete)%>

<% end %> 2 |
3 |

4 | <%= @prurl.url %>
5 | Are you sure you want to delete the repository post receive URL? 6 |

7 | <% form_tag url_for(:controller => 'repository_post_receive_urls', :action => 'destroy', :project_id => @project, :id => @prurl.id ), :method => :delete do %> 8 |

9 | 10 | <%= submit_tag l(:button_delete) %> 11 |

12 | <% end %> 13 |
14 | -------------------------------------------------------------------------------- /app/views/deployment_credentials/edit.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_deployment_credential_edit)%>

<% end %> 2 |
<%= error_messages_for 'cred' %>
3 | <% remote_form_for :deployment_credentials, @cred, 4 | :url => { :controller => 'deployment_credentials', :action => 'update', :project_id => @project, :id => @cred.id }, 5 | :html => {:method => :post} do |f| %> 6 |
7 | <%= "#{keylabel(@key)} => ".html_safe %> 8 | <%= f.select :perm, options_for_select(DeploymentCredential::valid_perms,@cred.perm), :required => true %> 9 |
10 | <%= submit_tag l(:button_save) %> 11 | <% end %> 12 | -------------------------------------------------------------------------------- /db/migrate/20120710204007_add_repository_mirror_fields.rb: -------------------------------------------------------------------------------- 1 | class AddRepositoryMirrorFields < ActiveRecord::Migration 2 | def self.up 3 | add_column :repository_mirrors, :push_mode, :integer, :default => 0 4 | add_column :repository_mirrors, :include_all_branches, :boolean, :default => false 5 | add_column :repository_mirrors, :include_all_tags, :boolean, :default => false 6 | add_column :repository_mirrors, :explicit_refspec, :string, :default => "" 7 | end 8 | 9 | def self.down 10 | remove_column :repository_mirrors, :push_mode 11 | remove_column :repository_mirrors, :include_all_branches 12 | remove_column :repository_mirrors, :include_all_tags 13 | remove_column :repository_mirrors, :explicit_refspec 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /assets/javascripts/notify_cia_test.js: -------------------------------------------------------------------------------- 1 | document.observe("dom:loaded", function() { 2 | console.log("Binding click event to test cia notification"); 3 | Event.observe("notify_cia_test", 'click', function(event) { 4 | console.log("Notification Test Link Clicked"); 5 | Event.stop(event); 6 | new Ajax.Request($('notify_cia_test').href, { 7 | onSuccess : function(transport) { 8 | console.log("Notification Test Result:", transport.responseText); 9 | $('notify_cia_result').update(transport.responseText) 10 | }, 11 | onFailure : function(transport) { 12 | console.log("Notification Test Failure:", transport); 13 | $('notify_cia_result').update(transport.responseText); 14 | } 15 | }) 16 | return false; 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'users_helper' 2 | 3 | # Add a tab for editing public keys to the user's page.... 4 | module GitHostingUsersHelperPatch 5 | def self.included(base) 6 | base.send(:include, InstanceMethods) 7 | base.class_eval do 8 | alias_method_chain :user_settings_tabs, :public_keys 9 | end 10 | end 11 | 12 | module InstanceMethods 13 | # Add a public_keys tab to the user administration page 14 | def user_settings_tabs_with_public_keys 15 | tabs = user_settings_tabs_without_public_keys 16 | tabs << { :name => 'keys', :partial => 'gitolite_public_keys/view', :label => :label_public_keys } 17 | return tabs 18 | end 19 | end 20 | end 21 | 22 | 23 | UsersHelper.send(:include, GitHostingUsersHelperPatch) 24 | -------------------------------------------------------------------------------- /app/views/deployment_credentials/destroy.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr %>

<%=l(:label_deployment_credential_delete)%>

<% end %> 2 |
3 |

4 | <%= "#{keylabel(@cred.gitolite_public_key)} => #{@cred.perm}".html_safe %>
5 | Are you sure you want to delete this deployment credential? 6 | <% if @will_delete_key %> 7 |
(Will delete corresponding key: <%= "#{keylabel(@cred.gitolite_public_key)}".html_safe %>) 8 | <% end %> 9 |

10 | <% form_tag url_for(:controller => 'deployment_credentials', :action => 'destroy', :project_id => @project, :id => @cred.id ), :method => :delete do %> 11 |

12 | 13 | <%= submit_tag l(:button_delete) %> 14 |

15 | <% end %> 16 |
17 | -------------------------------------------------------------------------------- /app/views/repositories/edit.html.erb: -------------------------------------------------------------------------------- 1 |

<%= l(:label_repository) %>

2 | 3 | <% labelled_form_for :repository, @repository, :url => repository_path(@path), :html => {:method => :put} do |f| %> 4 | <%= render :partial => 'form', :locals => {:f => f} %> 5 | <% end %> 6 | 7 | 8 | <% if @repository && @repository.is_a?(Repository::Git) && !(@repository.nil? || @repository.new_record?) %> 9 | <% if GitHostingHelper.can_view_deployment_keys(@repository.project) %> 10 | <%= render :partial => 'deployment_credentials/view_list' %> 11 | <% end %> 12 | 13 | <% if GitHostingHelper.can_view_post_receive_urls(@repository.project) %> 14 | <%= render :partial => 'repository_post_receive_urls/view_list' %> 15 | <% end %> 16 | 17 | <% if GitHostingHelper.can_view_mirrors(@repository.project) %> 18 | <%= render :partial => 'repository_mirrors/view_list' %> 19 | <% end %> 20 | <% end %> 21 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/sys_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'sys_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module SysControllerPatch 9 | def fetch_changesets_with_disable_update 10 | # Turn of updates during repository update 11 | GitHostingObserver.set_update_active(false); 12 | 13 | # Do actual update 14 | fetch_changesets_without_disable_update 15 | 16 | # Perform the updating process on all projects 17 | GitHostingObserver.set_update_active(:resync_all); 18 | end 19 | 20 | def self.included(base) 21 | base.class_eval do 22 | unloadable 23 | end 24 | begin 25 | base.send(:alias_method_chain, :fetch_changesets, :disable_update) 26 | rescue 27 | end 28 | end 29 | end 30 | end 31 | end 32 | 33 | # Patch in changes 34 | SysController.send(:include, GitHosting::Patches::SysControllerPatch) 35 | -------------------------------------------------------------------------------- /app/views/cia_notification_mailer/cia_notification.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%= @plugin.name %> 4 | <%= @plugin.version %> 5 | <%= @plugin.url %> 6 | 7 | 8 | <%= @revision.project.name %> 9 | <%= @branch %> 10 | 11 | <%= @revision.committed_on.to_i %> 12 | 13 | 14 | <%= @revision.revision[0..10] %> 15 | <%= @revision.author %> 16 | <%= @revision.comments %> 17 | <%= url_for_revision(@revision) %> 18 | <%- if not @revision.changes.empty? -%> 19 | 20 | <%- @revision.changes.each do |change| -%> 21 | <%= change.path %> 22 | <%- end -%> 23 | 24 | <%- end -%> 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /app/views/repository_mirrors/push.html.erb: -------------------------------------------------------------------------------- 1 | <% if not @is_xhr -%> 2 | <%= stylesheet_link_tag('application', :plugin => 'redmine_git_hosting') %> 3 |

<%=l(:button_push_title)%>

4 | <%- end %> 5 |

6 | <%= l(:mirror_push_info_html, :mirror_url => @mirror.url, :status => @push_failed==true ? l(:mirror_push_fail) : l(:mirror_push_sucess)) %> 7 |

8 | <%= l(:mirror_push_output_label) %> 9 |
"><%= @shellout %>
10 | <% if @is_xhr && @push_failed==false -%> 11 | 26 | <%- end %> 27 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/groups_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'groups_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module GroupsControllerPatch 9 | 10 | @@original_projects = [] 11 | 12 | def disable_git_observer_updates 13 | GitHostingObserver.set_update_active(false) 14 | end 15 | 16 | def do_single_update 17 | GitHostingObserver.set_update_active(true) 18 | end 19 | 20 | 21 | def self.included(base) 22 | base.class_eval do 23 | unloadable 24 | end 25 | base.send(:before_filter, :disable_git_observer_updates, :only=>[:update, :destroy, :add_users, :remove_user, :edit_membership, :destroy_membership]) 26 | base.send(:after_filter, :do_single_update, :only=>[:update, :destroy, :add_users, :remove_user, :edit_membership, :destroy_membership]) 27 | end 28 | end 29 | end 30 | end 31 | 32 | # Patch in changes 33 | GroupsController.send(:include, GitHosting::Patches::GroupsControllerPatch) 34 | -------------------------------------------------------------------------------- /assets/javascripts/zero_clipboard_setup.js: -------------------------------------------------------------------------------- 1 | var zero_clipboard_source_input_control_id = "git_url_text"; 2 | var clipboard = null 3 | 4 | function reset_zero_clipboard() 5 | { 6 | var clip_container = $('clipboard_container'); 7 | if (clip_container) { 8 | clip_container.show(); 9 | clip_container.style.fontFamily="serif" 10 | 11 | var cur_children = clip_container.childNodes; 12 | var ci; 13 | for(ci=0; ci< cur_children.length; ci++) 14 | { 15 | var c = cur_children[ci]; 16 | if(c.id != "clipboard_button") 17 | { 18 | clip_container.removeChild(c); 19 | } 20 | } 21 | 22 | clipboard = new ZeroClipboard.Client(); 23 | 24 | clipboard.setHandCursor(true); 25 | clipboard.glue('clipboard_button', 'clipboard_container'); 26 | 27 | clipboard.addEventListener('mouseOver', function (client) { 28 | clipboard.setText($(zero_clipboard_source_input_control_id).value); 29 | }); 30 | } 31 | } 32 | 33 | function setZeroClipboardInputSource(id) 34 | { 35 | zero_clipboard_source_input_control_id = id; 36 | } 37 | 38 | document.observe("dom:loaded", function() { reset_zero_clipboard(); } ) 39 | -------------------------------------------------------------------------------- /db/migrate/20120708070841_add_settings_to_plugin_4.rb: -------------------------------------------------------------------------------- 1 | class AddSettingsToPlugin4 < ActiveRecord::Migration 2 | def self.up 3 | begin 4 | # Add some new settings to settings page, if they don't exist 5 | valuehash = (Setting.plugin_redmine_git_hosting).clone 6 | valuehash['gitForceHooksUpdate'] ||= 'true' 7 | if (Setting.plugin_redmine_git_hosting != valuehash) 8 | say "Added redmine_git_hosting settings: 'gitForceHooksUpdate'." 9 | Setting.plugin_redmine_git_hosting = valuehash 10 | end 11 | rescue => e 12 | # ignore problems if plugin settings don't exist yet 13 | end 14 | end 15 | 16 | def self.down 17 | begin 18 | # Remove above settings from plugin page 19 | valuehash = (Setting.plugin_redmine_git_hosting).clone 20 | valuehash.delete('gitForceHooksUpdate') 21 | 22 | if (Setting.plugin_redmine_git_hosting != valuehash) 23 | say "Removed redmine_git_hosting settings: 'gitForceHooksUpdate'." 24 | Setting.plugin_redmine_git_hosting = valuehash 25 | end 26 | rescue => e 27 | # ignore problems if table doesn't exist yet.... 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/views/layouts/popup.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= begin csrf_meta_tag; rescue; "" end %> 8 | <%= favicon %> 9 | <%= stylesheet_link_tag 'application', :media => 'all' %> 10 | <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %> 11 | <%= defined?(javascript_heads)? javascript_heads : javascript_include_tag(:defaults) %> 12 | <%= heads_for_theme %> 13 | 19 | <%= call_hook :view_layouts_base_html_head %> 20 | 21 | 22 | <%= render_flash_messages %> 23 | <%= yield %> 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/repository_cia_filters.rb: -------------------------------------------------------------------------------- 1 | module GitHosting 2 | module Patches 3 | module RepositoryCiaFilters 4 | module FilterMethods 5 | def notified(scmid) 6 | scmid = scmid.scmid if scmid.instance_of?(Changeset) 7 | #self.cia_notifications.push GitCiaNotification.new(:scmid => scmid) 8 | self.push GitCiaNotification.new(:scmid => scmid) 9 | end 10 | 11 | def notified?(scmid) 12 | #GitHosting.logger.debug "1On has_notified? processing smcid: #{scmid}" 13 | scmid = scmid.scmid if scmid.instance_of?(Changeset) 14 | #GitHosting.logger.debug "2On has_notified? processing smcid: #{scmid}" 15 | #q = find(:first, :conditions => ["scmid = ?", scmid]) 16 | #GitHosting.logger.debug "Query result: #{q}, NIL? #{q.nil?}" 17 | #return !q.nil? 18 | #return !self.cia_notifications.find(:first, :conditions => ["scmid = ?", scmid_]).nil? 19 | return !find(:first, :conditions => ["scmid = ?", scmid]).nil? 20 | end 21 | end 22 | def self.included(base) 23 | base.class_eval do 24 | unloadable 25 | end 26 | base.extend(FilterMethods) 27 | end 28 | end 29 | end 30 | end 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/helpers/gitolite_public_keys_helper.rb: -------------------------------------------------------------------------------- 1 | module GitolitePublicKeysHelper 2 | def gitolite_public_keys_status_options_for_select(user, selected) 3 | key_count_by_active = user.gitolite_public_keys.count(:group => 'active').to_hash 4 | options_for_select([[l(:label_all), nil], 5 | ["#{l(:status_active)} (#{key_count_by_active[true].to_i})", GitolitePublicKey::STATUS_ACTIVE], 6 | ["#{l(:status_locked)} (#{key_count_by_active[false].to_i})", GitolitePublicKey::STATUS_LOCKED]], selected) 7 | end 8 | 9 | def keylabel(key) 10 | if key.user == User.current 11 | "\"#{key.title}\"" 12 | else 13 | "\"#{key.user.login}@#{key.title}\"" 14 | end 15 | end 16 | 17 | def keylabel_text(key) 18 | if key.user == User.current 19 | "#{key.title}" 20 | else 21 | "#{key.user.login}@#{key.title}" 22 | end 23 | end 24 | 25 | def wrap_and_join(in_array,my_or="or") 26 | my_array = in_array.map{|x| "\"#{x}\""} 27 | length = my_array.length 28 | return my_array if length < 2 29 | my_array[length-1] = my_or+" "+my_array[length-1] 30 | if length == 2 31 | my_array.join(' ') 32 | else 33 | my_array.join(', ') 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /selinux/redmine_git.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | DIRNAME=`dirname $0` 4 | cd $DIRNAME 5 | USAGE="$0 [ --update ]" 6 | if [ `id -u` != 0 ]; then 7 | echo 'You must be root to run this script' 8 | exit 1 9 | fi 10 | 11 | if [ $# -eq 1 ]; then 12 | if [ "$1" = "--update" ] ; then 13 | time=`ls -l --time-style="+%x %X" redmine_git.te | awk '{ printf "%s %s", $6, $7 }'` 14 | rules=`ausearch --start $time -m avc --raw -se redmine_git` 15 | if [ x"$rules" != "x" ] ; then 16 | echo "Found avc's to update policy with" 17 | echo -e "$rules" | audit2allow -R 18 | echo "Do you want these changes added to policy [y/n]?" 19 | read ANS 20 | if [ "$ANS" = "y" -o "$ANS" = "Y" ] ; then 21 | echo "Updating policy" 22 | echo -e "$rules" | audit2allow -R >> redmine_git.te 23 | # Fall though and rebuild policy 24 | else 25 | exit 0 26 | fi 27 | else 28 | echo "No new avcs found" 29 | exit 0 30 | fi 31 | else 32 | echo -e $USAGE 33 | exit 1 34 | fi 35 | elif [ $# -ge 2 ] ; then 36 | echo -e $USAGE 37 | exit 1 38 | fi 39 | 40 | echo "Building and Loading Policy" 41 | set -x 42 | make -f /usr/share/selinux/devel/Makefile 43 | /usr/sbin/semodule -i redmine_git.pp 44 | 45 | -------------------------------------------------------------------------------- /app/models/repository_post_receive_url.rb: -------------------------------------------------------------------------------- 1 | class RepositoryPostReceiveUrl < ActiveRecord::Base 2 | STATUS_ACTIVE = 1 3 | STATUS_INACTIVE = 0 4 | 5 | belongs_to :repository 6 | 7 | attr_accessible :url, :mode, :active 8 | 9 | validates_uniqueness_of :url, :scope => [:repository_id] 10 | validates_presence_of :repository_id 11 | validates_format_of :url, :with => URI::regexp(%w(http https)) 12 | validates_associated :repository 13 | 14 | before_validation :strip_whitespace 15 | 16 | named_scope :active, {:conditions => {:active => RepositoryPostReceiveUrl::STATUS_ACTIVE}} 17 | named_scope :inactive, {:conditions => {:active => RepositoryPostReceiveUrl::STATUS_INACTIVE}} 18 | 19 | validates_inclusion_of :mode, :in => [:github, :get] 20 | def mode 21 | read_attribute(:mode).to_sym rescue nil 22 | end 23 | def mode= (value) 24 | write_attribute(:mode, (value.to_sym && value.to_sym.to_s rescue nil)) 25 | end 26 | 27 | def to_s 28 | return File.join("#{repository.project.identifier}-#{url}") 29 | end 30 | 31 | protected 32 | 33 | # Strip leading and trailing whitespace 34 | def strip_whitespace 35 | self.url = url.strip 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/views/settings/_display_access.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

<%= l(:label_current_access_patterns) %>

3 | <%= l(:display_access_setup, :type => (GitHosting.multi_repos? ? "default " : "")) %>: 4 |

5 | 6 | <%= "~#{@settings['gitUser']}/#{@settings['gitRepositoryBasePath']}#{@settings['gitRedmineSubdir']}#{(GitHosting.repository_hierarchy)?'project1/project2/':''}project3.git" 7 | %> 8 |
9 | 10 | <% gitSHP = Setting.plugin_redmine_git_hosting['gitServer'].match(/:\d+$/) %> 11 | <%= "#{gitSHP ? 'ssh://' : ''}#{@settings['gitUser']}@#{@settings['gitServer']}#{gitSHP ? '/' : ':'}#{@settings['gitRedmineSubdir']}#{(GitHosting.repository_hierarchy)?'project1/project2/':''}project3.git" 12 | %> 13 |
14 | 15 | <%= "http://redmine-user@#{GitHosting.my_root_url}/#{GitHosting.http_server_subdir}#{(GitHosting.repository_hierarchy)?'project1/project2/':''}project3.git" 16 | %> 17 |

18 | <%= l(:display_access_emphasis) %> 19 | <%= @settings['gitRepositoryHierarchy']=='true' ? l(:display_access_hierarchical) : l(:display_access_flat) %> 20 |
21 | -------------------------------------------------------------------------------- /db/migrate/20120724211806_add_settings_to_plugin_5.rb: -------------------------------------------------------------------------------- 1 | class AddSettingsToPlugin5 < ActiveRecord::Migration 2 | def self.up 3 | begin 4 | # Add some new settings to settings page, if they don't exist 5 | valuehash = (Setting.plugin_redmine_git_hosting).clone 6 | valuehash['gitConfigFile'] ||= 'gitolite.conf' 7 | valuehash['gitConfigHasAdminKey'] ||= 'true' 8 | 9 | if (Setting.plugin_redmine_git_hosting != valuehash) 10 | say "Added redmine_git_hosting settings: 'gitConfigFile', 'gitConfigHasAdminKey'" 11 | Setting.plugin_redmine_git_hosting = valuehash 12 | end 13 | rescue => e 14 | # ignore problems if plugin settings don't exist yet 15 | end 16 | end 17 | 18 | def self.down 19 | begin 20 | # Remove above settings from plugin page 21 | valuehash = (Setting.plugin_redmine_git_hosting).clone 22 | valuehash.delete('gitConfigFile') 23 | valuehash.delete('gitConfigHasAdminKey') 24 | 25 | if (Setting.plugin_redmine_git_hosting != valuehash) 26 | say "Removed redmine_git_hosting settings: 'gitConfigFile', 'gitConfigHasAdminKey" 27 | Setting.plugin_redmine_git_hosting = valuehash 28 | end 29 | rescue => e 30 | # ignore problems if table doesn't exist yet.... 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /db/migrate/20091119162426_set_mirror_role_permissions.rb: -------------------------------------------------------------------------------- 1 | class SetMirrorRolePermissions < ActiveRecord::Migration 2 | def self.up 3 | 4 | begin 5 | GitHostingObserver.set_update_active(false) 6 | 7 | manager_role = Role.find_by_name(I18n.t(:default_role_manager)) 8 | manager_role.add_permission! :view_repository_mirrors 9 | manager_role.add_permission! :edit_repository_mirrors 10 | manager_role.add_permission! :create_repository_mirrors 11 | manager_role.save 12 | 13 | developer_role = Role.find_by_name(I18n.t(:default_role_developer)) 14 | developer_role.add_permission! :view_repository_mirrors 15 | developer_role.save 16 | rescue 17 | end 18 | 19 | end 20 | 21 | def self.down 22 | 23 | begin 24 | GitHostingObserver.set_update_active(false) 25 | 26 | manager_role = Role.find_by_name(I18n.t(:default_role_manager)) 27 | manager_role.remove_permission! :view_repository_mirrors 28 | manager_role.remove_permission! :edit_repository_mirrors 29 | manager_role.remove_permission! :create_repository_mirrors 30 | manager_role.save 31 | 32 | developer_role = Role.find_by_name(I18n.t(:default_role_developer)) 33 | developer_role.remove_permission! :view_repository_mirrors 34 | developer_role.save 35 | rescue 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /db/migrate/2012052100001_set_post_receive_url_role_permissions.rb: -------------------------------------------------------------------------------- 1 | class SetPostReceiveUrlRolePermissions < ActiveRecord::Migration 2 | def self.up 3 | 4 | begin 5 | GitHostingObserver.set_update_active(false) 6 | 7 | manager_role = Role.find_by_name(I18n.t(:default_role_manager)) 8 | manager_role.add_permission! :view_repository_post_receive_urls 9 | manager_role.add_permission! :edit_repository_post_receive_urls 10 | manager_role.add_permission! :create_repository_post_receive_urls 11 | manager_role.save 12 | 13 | developer_role = Role.find_by_name(I18n.t(:default_role_developer)) 14 | developer_role.add_permission! :view_repository_post_receive_urls 15 | developer_role.save 16 | rescue 17 | end 18 | 19 | end 20 | 21 | def self.down 22 | 23 | begin 24 | GitHostingObserver.set_update_active(false) 25 | 26 | manager_role = Role.find_by_name(I18n.t(:default_role_manager)) 27 | manager_role.remove_permission! :view_repository_post_receive_urls 28 | manager_role.remove_permission! :edit_repository_post_receive_urls 29 | manager_role.remove_permission! :create_repository_post_receive_urls 30 | manager_role.save 31 | 32 | developer_role = Role.find_by_name(I18n.t(:default_role_developer)) 33 | developer_role.remove_permission! :view_repository_post_receive_urls 34 | developer_role.save 35 | rescue 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /db/migrate/20111123214911_add_settings_to_plugin.rb: -------------------------------------------------------------------------------- 1 | class AddSettingsToPlugin < ActiveRecord::Migration 2 | def self.up 3 | begin 4 | # Add some new settings to settings page, if they don't exist 5 | valuehash = (Setting.plugin_redmine_git_hosting).clone 6 | valuehash['gitRecycleBasePath'] ||= 'recycle_bin/' 7 | valuehash['gitRecycleExpireTime'] ||= '24.0' 8 | valuehash['gitLockWaitTime'] ||= '10' 9 | if (Setting.plugin_redmine_git_hosting != valuehash) 10 | Setting.plugin_redmine_git_hosting = valuehash 11 | say "Added redmine_git_hosting settings: 'gitRecycleBasePath', 'getRecycleExpireTime', 'getLockWaitTime'" 12 | end 13 | rescue 14 | # ignore problems if plugin settings don't exist yet 15 | end 16 | end 17 | 18 | def self.down 19 | begin 20 | # Remove above settings from plugin page 21 | valuehash = (Setting.plugin_redmine_git_hosting).clone 22 | valuehash.delete('gitRecycleBasePath') 23 | valuehash.delete('gitRecycleExpireTime') 24 | valuehash.delete('gitLockWaitTime') 25 | if (Setting.plugin_redmine_git_hosting != valuehash) 26 | Setting.plugin_redmine_git_hosting = valuehash 27 | say "Removed redmine_git_hosting settings: 'gitRecycleBasePath', 'getRecycleExpireTime', 'getLockWaitTime'" 28 | end 29 | Setting.plugin_redmine_git_hosting = valuehash 30 | rescue 31 | # ignore problems if table doesn't exist yet.... 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | .icon-push 2 | { 3 | background-image: url("../images/icon-divide.png"); 4 | } 5 | 6 | pre.mirror-push { 7 | margin: 0 1em 1em 0em; 8 | padding: 2px; 9 | background-color: #fafafa; 10 | border: 1px solid #dadada; 11 | width:auto; 12 | overflow-x: auto; 13 | overflow-y: hidden; 14 | } 15 | 16 | pre.mirror-push.error { 17 | border: 1px solid #D71E1E; 18 | } 19 | 20 | .git_hosting_access_box { 21 | border: 2px solid; 22 | background-color: #dfffdf; 23 | border-color: #9fcf9f; 24 | color: #005f00; 25 | margin: -6px; 26 | padding: 4px; 27 | } 28 | .git_hosting_access_box p { 29 | padding-left:100px; 30 | margin-top: 8px; 31 | margin-bottom: 8px; 32 | } 33 | .git_hosting_access_box label { margin-left: -100px; width:95px; } 34 | .git_hosting_access_box em { 35 | font-style: italic; 36 | font-weight: bold; 37 | font-size: 110%; 38 | color: #467AA7; 39 | } 40 | 41 | .public_key_view { 42 | background-color: #fff; 43 | padding:5px; 44 | } 45 | 46 | .public_key_view label { 47 | font-weight: bold; 48 | text-align: right; 49 | } 50 | 51 | .public_key_view .myhead { 52 | font-style: italic; 53 | color: #000; 54 | size: 140%; 55 | padding: 10px 10px 0px 0px; 56 | margin-bottom: 5px; 57 | border-bottom: 1px solid #bbbbbb; 58 | } 59 | 60 | .public_key_view p { 61 | margin-top: 0px; 62 | margin-bottom: 5px; 63 | padding-top: 0px; 64 | } 65 | 66 | .public_key_view legend { 67 | font-style: italic; 68 | color: #000; 69 | size: 130%; 70 | } 71 | -------------------------------------------------------------------------------- /app/views/repository_mirrors/_form.html.erb: -------------------------------------------------------------------------------- 1 |
<%= error_messages_for 'mirror' %>
2 | 3 |
4 | 5 |

<%= f.text_field :url, :required => true, :size => 65 %>

6 |

<%= f.select :push_mode, options_for_select([[l(:label_full_mirror),0],[l(:label_forced_update),1],[l(:label_fast_forward),2]], @mirror.push_mode), :label => :label_push_mode %> 7 |

12 |

<%= f.check_box :active %>

13 |
14 | 15 | 16 | 37 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/my_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'my_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module MyControllerPatch 9 | # Add in values for viewing public keys: 10 | def account_with_public_keys 11 | # Previous routine 12 | account_without_public_keys 13 | 14 | @gitolite_user_keys = @user.gitolite_public_keys.active.user_key.find(:all,:order => 'title ASC, created_at ASC') 15 | @gitolite_deploy_keys = @user.gitolite_public_keys.active.deploy_key.find(:all,:order => 'title ASC, created_at ASC') 16 | @gitolite_public_keys = @gitolite_user_keys + @gitolite_deploy_keys 17 | @gitolite_public_key = @gitolite_public_keys.detect{|x| x.id == params[:public_key_id].to_i} 18 | if @gitolite_public_key.nil? 19 | if params[:public_key_id] 20 | # public_key specified that doesn't belong to @user. Kill off public_key_id and try again 21 | redirect_to :public_key_id => nil, :tab => nil 22 | return 23 | else 24 | @gitolite_public_key = GitolitePublicKey.new 25 | end 26 | end 27 | end 28 | 29 | def self.included(base) 30 | base.class_eval do 31 | unloadable 32 | 33 | helper :gitolite_public_keys 34 | end 35 | begin 36 | base.send(:alias_method_chain, :account, :public_keys) 37 | rescue 38 | end 39 | end 40 | end 41 | end 42 | end 43 | 44 | # Patch in changes 45 | MyController.send(:include, GitHosting::Patches::MyControllerPatch) 46 | -------------------------------------------------------------------------------- /tasks/gitolite.rake: -------------------------------------------------------------------------------- 1 | # 2 | # WARNING: Tasks in this file have been deprecated. See redmine_git_hosting.rake. 3 | # 4 | # There are two tasks here of interest: gitolite:update_repositories and gitolite:fetch_changesets. 5 | # The second includes the first (since fetching of changesets causes updating of gitolite config). 6 | # 7 | # As of the most recent release, either of these will complete resynchronize the gitolite configuration 8 | # and can thus be used to recover from errors that might have been introduced by sychronization errors. 9 | # 10 | # Specifically: 11 | # 12 | # 1) Resynchronize gitolite configuration (fix keys directory and configuration). Also, expire 13 | # repositories in the recycle_bin if time. 14 | # 15 | # rake gitolite:update_repositories RAILS_ENV=xxx 16 | # 17 | # 2) Fetch all changesets for repositories and then rescynronize gitolite configuration (as in #1) 18 | # 19 | # rake gitolite:fetch_changes RAILS_ENV=xxx 20 | # 21 | namespace :gitolite do 22 | desc "Update/repair gitolite configuration" 23 | task :update_repositories => [:environment] do 24 | puts "WARNING: This task deprecated. Use 'rake redmine_git_hosting:update_repositories' instead." 25 | Rake::Task["redmine_git_hosting:update_repositories"].invoke 26 | end 27 | desc "Fetch commits from gitolite repositories/update gitolite configuration" 28 | task :fetch_changes => [:environment] do 29 | puts "WARNING: This task deprecated. Use 'rake redmine_git_hosting:fetch_changesets' instead." 30 | Rake::Task["redmine_git_hosting:fetch_changesets"].invoke 31 | end 32 | 33 | end 34 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/git_repository_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'repository' 5 | require_dependency 'repository/git' 6 | 7 | module GitHosting 8 | module Patches 9 | module GitRepositoryPatch 10 | 11 | # Set up git urls for new repositories 12 | def set_git_urls 13 | self.url = GitHosting.repository_path(self) if url.blank? 14 | self.root_url = url if root_url.blank? 15 | end 16 | 17 | def report_last_commit_with_always_true 18 | true 19 | end 20 | def extra_report_last_commit_with_always_true 21 | true 22 | end 23 | 24 | def fetch_changesets_with_disable_update 25 | # Turn of updates during repository update 26 | GitHostingObserver.set_update_active(false); 27 | 28 | # Do actual update 29 | fetch_changesets_without_disable_update 30 | 31 | # Reenable updates to perform a single update 32 | GitHostingObserver.set_update_active(true); 33 | end 34 | 35 | def self.included(base) 36 | base.class_eval do 37 | unloadable 38 | 39 | before_validation :set_git_urls 40 | end 41 | 42 | begin 43 | base.send(:alias_method_chain, :report_last_commit, :always_true) 44 | base.send(:alias_method_chain, :extra_report_last_commit, :always_true) 45 | base.send(:alias_method_chain, :fetch_changesets, :disable_update) 46 | rescue 47 | end 48 | 49 | end 50 | end 51 | end 52 | end 53 | 54 | # Patch in changes 55 | Repository::Git.send(:include, GitHosting::Patches::GitRepositoryPatch) 56 | -------------------------------------------------------------------------------- /app/models/git_repository_extra.rb: -------------------------------------------------------------------------------- 1 | require 'digest/sha1' 2 | 3 | class GitRepositoryExtra < ActiveRecord::Base 4 | 5 | belongs_to :repository, :class_name => 'Repository', :foreign_key => 'repository_id' 6 | validates_associated :repository 7 | attr_accessible :id, :repository_id, :key, :git_http, :git_daemon, :notify_cia 8 | 9 | def after_initialize 10 | if self.repository.nil? 11 | generate 12 | setup_defaults 13 | end 14 | end 15 | 16 | def validate_encoded_time(clear_time, encoded_time) 17 | valid = false 18 | begin 19 | cur_time_seconds = Time.new.utc.to_i 20 | test_time_seconds = clear_time.to_i 21 | if cur_time_seconds - test_time_seconds < 5*60 22 | key = read_attribute(:key) 23 | test_encoded = Digest::SHA1.hexdigest(clear_time.to_s + key.to_s) 24 | if test_encoded.to_s == encoded_time.to_s 25 | valid = true 26 | end 27 | end 28 | rescue Exception=>e 29 | end 30 | valid 31 | end 32 | 33 | def generate 34 | if self.key.nil? 35 | write_attribute(:key, (0...64+rand(64) ).map{65.+(rand(25)).chr}.join ) 36 | end 37 | end 38 | 39 | def setup_defaults 40 | write_attribute(:git_http,Setting.plugin_redmine_git_hosting['gitHttpDefault']) if Setting.plugin_redmine_git_hosting['gitHttpDefault'] 41 | write_attribute(:git_daemon,Setting.plugin_redmine_git_hosting['gitDaemonDefault']) if Setting.plugin_redmine_git_hosting['gitDaemonDefault'] 42 | write_attribute(:notify_cia,Setting.plugin_redmine_git_hosting['gitNotifyCIADefault']) if Setting.plugin_redmine_git_hosting['gitNotifyCIADefault'] 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /db/migrate/2011081700000_move_notified_cia_to_git_cia_notifications.rb: -------------------------------------------------------------------------------- 1 | class MoveNotifiedCiaToGitCiaNotifications < ActiveRecord::Migration 2 | def self.up 3 | 4 | drop_table :git_cia_notifications if self.table_exists?("git_cia_notifications") 5 | 6 | create_table :git_cia_notifications do |t| 7 | t.column :repository_id, :integer 8 | t.column :scmid, :string 9 | end 10 | 11 | # Speed up searches 12 | add_index(:git_cia_notifications, :scmid) 13 | # Make sure uniqueness of the two columns, :scmid, :repository_id 14 | add_index(:git_cia_notifications, [:scmid, :repository_id], :unique => true) 15 | 16 | Project.find(:all).each {|project| 17 | if project.repository.is_a?(Repository::Git) 18 | project.repository.changesets.each { |changeset| 19 | if changeset.respond_to?(:notified_cia) and changeset.notified_cia == 1 20 | #project.repository.set_notified(changeset) 21 | project.repository.cia_notifications.push GitCiaNotification.new(:scmid => changeset.scmid) 22 | #cia_notification = GitCiaNotification.new 23 | #cia_notification.scmid = changeset.scmid 24 | #cia_notification.repository = project.repository 25 | #cia_notification.save 26 | end 27 | } 28 | end 29 | } 30 | remove_column :changesets, :notified_cia if self.column_exists?(:changesets, :notified_cia) 31 | end 32 | 33 | def self.down 34 | drop_table :git_cia_notifications 35 | end 36 | 37 | def self.table_exists?(name) 38 | ActiveRecord::Base.connection.tables.include?(name) 39 | end 40 | 41 | def self.column_exists?(table_name, column_name) 42 | columns(table_name).any?{ |c| c.name == column_name.to_s } 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /assets/javascripts/git_url_display.js: -------------------------------------------------------------------------------- 1 | var allGitUrlIds = ["git_url_ssh", "git_url_http", "git_url_git"] 2 | function updateGitUrl(el) 3 | { 4 | guHttpBase = guHttpBase.replace(/\/$/, "") 5 | 6 | var urls=[] 7 | var gitSHP = /:\d+$/.test(guGitServer) 8 | urls["git_url_ssh"] = [(gitSHP ? "ssh://" : "") + guGitUser + "@" + guGitServer + (gitSHP ? "/" : ":") + guSshURL, guUserIsCommitter] 9 | urls["git_url_http"] = [guHttpProto + "://" + ( (!guProjectIsPublic) || guUserIsCommitter ? encodeURIComponent(guUser) + "@" : "") + guHttpBase + "/" + guHttpURL, guUserIsCommitter] 10 | urls["git_url_git"] = ["git://" + guGitServer + "/" + guSshURL, false] 11 | var allGitUrlIds = ["git_url_ssh", "git_url_http", "git_url_git"] 12 | 13 | var selected_id = el.id 14 | document.getElementById("git_url_text").value = urls[selected_id][0]; 15 | document.getElementById("git_url_access").innerHTML = urls[selected_id][1] ? "Read+Write" : "Read-Only" 16 | 17 | var i 18 | for(i=0;i e 20 | # ignore problems if plugin settings don't exist yet 21 | end 22 | end 23 | 24 | def self.down 25 | begin 26 | # Remove above settings from plugin page 27 | valuehash = (Setting.plugin_redmine_git_hosting).clone 28 | valuehash.delete('gitDaemonDefault') 29 | valuehash.delete('gitHttpDefault') 30 | valuehash.delete('gitNotifyDIADefault') 31 | valuehash.delete('gitTempDataDir') 32 | valuehash.delete('gitScriptDir') 33 | 34 | if (Setting.plugin_redmine_git_hosting != valuehash) 35 | say "Removed redmine_git_hosting settings: 'gitDaemonDefault', 'gitHttpDefault', 'gitNotifyCIADefault', 'gitTempDataDir', 'gitScriptDir'" 36 | Setting.plugin_redmine_git_hosting = valuehash 37 | end 38 | rescue => e 39 | # ignore problems if table doesn't exist yet.... 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/views/my/account.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to(l(:button_change_password), {:action => 'password'}, :class => 'icon icon-passwd') if @user.change_password_allowed? %> 3 | <%= call_hook(:view_my_account_contextual, :user => @user)%> 4 |
5 |

<%=l(:label_my_account)%>

6 | <%= error_messages_for 'user' %> 7 | 8 |
9 | <% labelled_form_for :user, @user, 10 | :url => { :action => "account" }, 11 | :html => { :id => 'my_account_form', 12 | :method => :post } do |f| %> 13 |
14 | <%=l(:label_information_plural)%> 15 |

<%= f.text_field :firstname, :required => true %>

16 |

<%= f.text_field :lastname, :required => true %>

17 |

<%= f.text_field :mail, :required => true %>

18 |

<%= f.select :language, lang_options_for_select %>

19 | <% if Setting.openid? %> 20 |

<%= f.text_field :identity_url %>

21 | <% end %> 22 | <% @user.custom_field_values.select(&:editable?).each do |value| %> 23 |

<%= custom_field_tag_with_label :user, value %>

24 | <% end %> 25 | <%= call_hook(:view_my_account, :user => @user, :form => f) %> 26 |
27 |
28 | <%=l(:field_mail_notification)%> 29 | <%= render :partial => 'users/mail_notifications' %> 30 |
31 |
32 | <%=l(:label_preferences)%> 33 | <%= render :partial => 'users/preferences' %> 34 | 35 | <%= submit_tag l(:button_save) %> 36 |
37 | <% end %> 38 |
39 | 40 |
41 | <%= render :partial => 'gitolite_public_keys/view' %> 42 |
43 | 44 | <% content_for :sidebar do %> 45 | <%= render :partial => 'my/sidebar' %> 46 | <% end %> 47 | <% html_title(l(:label_my_account)) -%> 48 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/project_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'project' 5 | 6 | module GitHosting 7 | module Patches 8 | module ProjectPatch 9 | # Find all repositories owned by project which are Repository::Git 10 | # Works for both multi- and single- repo/project 11 | def gl_repos 12 | all_repos.select{|x| x.is_a?(Repository::Git)} 13 | end 14 | 15 | # Find all repositories owned by project. Works for both multi- and 16 | # single- repo/project 17 | def all_repos 18 | if GitHosting.multi_repos? 19 | repositories 20 | else 21 | [ repository ].compact 22 | end 23 | end 24 | 25 | # Return first repo with a blank identifier (should be only one!) 26 | def repo_blank_ident 27 | Repository.find_by_project_id(id,:conditions => ["identifier = '' or identifier is null"]) 28 | end 29 | 30 | # Make sure that identifier does not match existing repository identifier 31 | # Only for Redmine 1.4 32 | def additional_ident_constraints 33 | if new_record? && !identifier.blank? && Repository.find_by_identifier_and_type(identifier,"Git") 34 | errors.add(:identifier,:ident_not_unique) 35 | end 36 | end 37 | 38 | def self.included(base) 39 | base.class_eval do 40 | unloadable 41 | 42 | named_scope :archived, { :conditions => {:status => "#{Project::STATUS_ARCHIVED}"}} 43 | named_scope :active_or_archived, { :conditions => "status=#{Project::STATUS_ACTIVE} OR status=#{Project::STATUS_ARCHIVED}" } 44 | 45 | # Place additional constraints on repository identifiers 46 | # Only for Redmine 1.4+ 47 | if GitHosting.multi_repos? 48 | validate :additional_ident_constraints 49 | end 50 | end 51 | end 52 | end 53 | end 54 | end 55 | 56 | # Patch in changes 57 | Project.send(:include, GitHosting::Patches::ProjectPatch) 58 | -------------------------------------------------------------------------------- /db/migrate/20120803043256_create_deployment_credentials.rb: -------------------------------------------------------------------------------- 1 | class CreateDeploymentCredentials < ActiveRecord::Migration 2 | def self.up 3 | begin 4 | create_table :deployment_credentials do |t| 5 | t.references :repository 6 | t.references :gitolite_public_key 7 | t.references :user 8 | t.column :active, :integer, :default => 1 9 | t.column :perm, :string, :null => false 10 | end 11 | add_index :deployment_credentials, :repository_id 12 | add_index :deployment_credentials, :gitolite_public_key_id 13 | 14 | add_column :gitolite_public_keys, :key_type, :integer, :default => GitolitePublicKey::KEY_TYPE_USER 15 | add_column :gitolite_public_keys, :delete_when_unused, :boolean, :default => true 16 | 17 | GitHostingObserver.set_update_active(false) 18 | 19 | manager_role = Role.find_by_name(I18n.t(:default_role_manager)) 20 | manager_role.add_permission! :view_deployment_keys 21 | manager_role.add_permission! :edit_deployment_keys 22 | manager_role.add_permission! :create_deployment_keys 23 | manager_role.save 24 | 25 | developer_role = Role.find_by_name(I18n.t(:default_role_developer)) 26 | developer_role.add_permission! :view_deployment_keys 27 | developer_role.save 28 | rescue => e 29 | puts "#{e}" 30 | end 31 | end 32 | 33 | def self.down 34 | begin 35 | drop_table :deployment_credentials 36 | remove_column :gitolite_public_keys, :key_type 37 | remove_column :gitolite_public_keys, :delete_when_unused 38 | 39 | GitHostingObserver.set_update_active(false) 40 | manager_role = Role.find_by_name(I18n.t(:default_role_manager)) 41 | manager_role.remove_permission! :view_deployment_keys 42 | manager_role.remove_permission! :edit_deployment_keys 43 | manager_role.remove_permission! :create_deployment_keys 44 | manager_role.save 45 | 46 | developer_role = Role.find_by_name(I18n.t(:default_role_developer)) 47 | developer_role.remove_permission! :view_deployment_keys 48 | developer_role.save 49 | rescue => e 50 | puts "#{e}" 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /db/migrate/20111220055819_add_settings_to_plugin_2.rb: -------------------------------------------------------------------------------- 1 | class AddSettingsToPlugin2 < ActiveRecord::Migration 2 | def self.up 3 | begin 4 | # Add some new settings to settings page, if they don't exist 5 | valuehash = (Setting.plugin_redmine_git_hosting).clone 6 | valuehash['httpServerSubdir'] ||= '' 7 | valuehash['gitRedmineSubdir'] ||= '' 8 | valuehash['gitRepositoryHierarchy'] ||= 'true' 9 | 10 | # Fix httpServer by removing directory components 11 | valuehash['httpServer'] = (valuehash['httpServer'][/^[^\/]*/]) 12 | 13 | if (Setting.plugin_redmine_git_hosting != valuehash) 14 | say "Added redmine_git_hosting settings: 'httpServerSubdir', 'gitRedmineSubdir', 'gitRepositoryHierarchy'" 15 | if (Setting.plugin_redmine_git_hosting['httpServer'] != valuehash['httpServer']) 16 | say "Updated 'httpServer' from '#{Setting.plugin_redmine_git_hosting['httpServer']}' to '#{valuehash['httpServer']}'." 17 | end 18 | Setting.plugin_redmine_git_hosting = valuehash 19 | end 20 | rescue => e 21 | # ignore problems if plugin settings don't exist yet 22 | end 23 | end 24 | 25 | def self.down 26 | begin 27 | # Remove above settings from plugin page 28 | valuehash = (Setting.plugin_redmine_git_hosting).clone 29 | valuehash.delete('httpServerSubdir') 30 | valuehash.delete('gitRedmineSubdir') 31 | valuehash.delete('gitRepositoryHierarchy') 32 | 33 | # Restore redmine root directory to httpServer (remove trailing '/') 34 | valuehash['httpServer'] = GitHosting.my_root_url 35 | 36 | if (Setting.plugin_redmine_git_hosting != valuehash) 37 | say "Removed redmine_git_hosting settings: 'httpServerSubdir', 'gitRedmineSubdir', 'gitRepositoryHierarchy'" 38 | if (Setting.plugin_redmine_git_hosting['httpServer'] != valuehash['httpServer']) 39 | say "Updated 'httpServer' from '#{Setting.plugin_redmine_git_hosting['httpServer']}' to '#{valuehash['httpServer']}'." 40 | end 41 | Setting.plugin_redmine_git_hosting = valuehash 42 | end 43 | rescue => e 44 | # ignore problems if table doesn't exist yet.... 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /assets/stylesheets/modalbox/modalbox.css: -------------------------------------------------------------------------------- 1 | #MB_overlay { 2 | position: absolute; 3 | margin: auto; 4 | top: 0; left: 0; 5 | width: 100%; height: 100%; 6 | z-index: 9999; 7 | background-color: #000!important; 8 | } 9 | #MB_overlay[id] { position: fixed; } 10 | 11 | #MB_window { 12 | position: absolute; 13 | top: 0; 14 | border: 0 solid; 15 | text-align: left; 16 | z-index: 10000; 17 | } 18 | #MB_window[id] { position: fixed!important; } 19 | 20 | #MB_frame { 21 | position: relative; 22 | background-color: #EFEFEF; 23 | height: 100%; 24 | } 25 | 26 | #MB_header { 27 | margin: 0; 28 | padding: 0; 29 | } 30 | 31 | #MB_content { 32 | padding: 6px .75em; 33 | overflow: auto; 34 | } 35 | 36 | #MB_caption { 37 | font: bold 100% "Lucida Grande", Arial, sans-serif; 38 | text-shadow: #FFF 0 1px 0; 39 | padding: .5em 2em .5em .75em; 40 | margin: 0; 41 | text-align: left; 42 | } 43 | 44 | #MB_close { 45 | display: block; 46 | position: absolute; 47 | right: 5px; top: 4px; 48 | padding: 2px 3px; 49 | font-weight: bold; 50 | text-decoration: none; 51 | font-size: 13px; 52 | } 53 | #MB_close:hover { 54 | background: transparent; 55 | } 56 | 57 | #MB_loading { 58 | padding: 1.5em; 59 | text-indent: -10000px; 60 | background: transparent url(spinner.gif) 50% 0 no-repeat; 61 | } 62 | 63 | /* Color scheme */ 64 | #MB_frame { 65 | padding-bottom: 7px; 66 | -webkit-border-radius: 7px; 67 | -moz-border-radius: 7px; 68 | border-radius: 7px; 69 | } 70 | #MB_window { 71 | background-color: #EFEFEF; 72 | color: #000; 73 | -webkit-box-shadow: 0 8px 64px #000; 74 | -moz-box-shadow: 0 0 64px #000; 75 | box-shadow: 0 0 64px #000; 76 | 77 | -webkit-border-radius: 7px; 78 | -moz-border-radius: 7px; 79 | border-radius: 7px; 80 | } 81 | #MB_content { border-top: 1px solid #F9F9F9; } 82 | #MB_header { 83 | background-color: #DDD; 84 | border-bottom: 1px solid #CCC; 85 | } 86 | #MB_caption { color: #000 } 87 | #MB_close { color: #777 } 88 | #MB_close:hover { color: #000 } 89 | 90 | 91 | /* Alert message */ 92 | .MB_alert { 93 | margin: 10px 0; 94 | text-align: center; 95 | } -------------------------------------------------------------------------------- /app/views/repositories/git_instructions.html.erb: -------------------------------------------------------------------------------- 1 | <%# This is used to display basic git setup instructions, like on github... %> 2 | <% flash.now[:warning] = "Repository is empty. Get started by following the instructions below." %> 3 | 4 | <%= render :partial => 'git_urls', :locals => {:repository => @repository, :project => @project} %> 5 | 6 | <% gitSHP = Setting.plugin_redmine_git_hosting['gitServer'].match(/:\d+$/) %> 7 | <% git_ssh_url = 8 | "#{gitSHP ? 'ssh://' : ''}#{Setting.plugin_redmine_git_hosting['gitUser']}@#{Setting.plugin_redmine_git_hosting['gitServer']}#{gitSHP ? '/' : ':'}#{GitHosting.repository_name(@repository)}.git" %> 9 | 10 |
11 | 12 |

Git Setup:

13 |
	Download and Install Git
14 | 	git config --global user.name "<%= User.current.name(:firstname_lastname) %>"
15 | 	git config --global user.email <%= User.current.mail %>
16 | <% if User.current.gitolite_public_keys.active.length == 0 %><%= "\t" + (link_to "Upload SSH Public Key", {:controller => 'my', :action => 'account'}) + "\n
" %><% else %><%= "" %><% end %> 17 |

Repository Setup:

18 |
	mkdir <%= @repository.git_name %>
19 | 	cd <%= @repository.git_name %>
20 | 	git init
21 | 	touch readme.txt
22 | 	git add readme.txt
23 | 	git commit -m 'Initializing <%= @repository.git_label %> repository'
24 | 	git remote add origin <%= git_ssh_url %>
25 | 	git push -u origin master
26 | 
27 |

Existing Git Repo?

28 |
	cd existing_git_repo
29 | 	git remote add origin <%= git_ssh_url %>
30 | 	git push -u origin master
31 | 
32 |
33 | 34 | <% if @repositories.size > 1 %> 35 | <% content_for :sidebar do %> 36 |

<%= l(:label_repository_plural) %>

37 |

38 | <%= @repositories.sort.collect {|repo| 39 | link_to h(repo.name), 40 | {:controller => 'repositories', :action => 'show', 41 | :id => @project, :repository_id => repo.identifier_param, :rev => nil, :path => nil}, 42 | :class => 'repository' + (repo == @repository ? ' selected' : '') 43 | }.join('
').html_safe %> 44 |

45 | <% end %> 46 | <% end %> 47 | -------------------------------------------------------------------------------- /app/views/deployment_credentials/_form_with_key.html.erb: -------------------------------------------------------------------------------- 1 |
<%= error_messages_for 'cred' %>
2 | 3 |
4 | 5 | <% labelled_fields_for :gitolite_public_key, @key do |k| %> 6 |
7 | <% if !@user_keys.empty? || !@other_keys.empty? %> 8 | <% option_array = [[l(:select_create_new_key),-1]] %> 9 | <% option_array += (@user_keys.map {|key| [keylabel_text(key),key.id]}) %> 10 | <% if !@other_keys.empty? %> 11 | <% option_array2 = (@other_keys.map {|key| [keylabel_text(key), key.id]}) %> 12 | <% maxlen = (option_array+option_array2).map{|x|x.first.length}.max %> 13 | <% extra = ([maxlen - l(:select_other_keys).length - 2,6].max)/2 %> 14 | <% option_array += [[("-"*extra)+" "+l(:select_other_keys)+" "+"-"*extra, -2]] %> 15 | <% option_array += option_array2 %> 16 | <% end %> 17 |

<%= k.select :id, options_for_select(option_array, :selected => -1, :disabled => [-2]+@disabled_keys.map(&:id)), :required => true, :label => :label_which_deploy_key %>

18 | <% end %> 19 |

<%= f.select :perm, options_for_select(DeploymentCredential::valid_perms,DeploymentCredential::default_perm), :required => true, :label => :label_deploy_perm %>

20 |
21 |
22 |
<%= l(:label_public_key_new) %> 23 |

<%= k.text_field :title, :label => :label_identifier_can_be_arbitrary, :required => true, :style => 'width:99%;' %>

24 |

<%= k.check_box :delete_when_unused, :required => true, :label => :field_delete_when_unused %>

25 |

<%= k.text_area :key, :required => true, :label => :label_cut_and_paste, 26 | :style => "width:99%;height:125px;overflow:auto;", 27 | :cols => nil, :rows => nil %>

28 |
29 |
30 | <% end %> 31 |
32 | 33 | 34 | 55 | -------------------------------------------------------------------------------- /assets/stylesheets/zero_clipboard.css: -------------------------------------------------------------------------------- 1 | #clipboard_button 2 | { 3 | background-color: #eee; 4 | background: url('../images/button.svg') 0 0 no-repeat; /* Opera needs an "image" :( - using svg for this so it will scale properly without looking too ugly */ 5 | background: -khtml-gradient(linear, left top, left bottom, from(#f8f8f8), to(#ddd)); /* Konquerer */ 6 | background: -moz-linear-gradient(top, #f8f8f8, #ddd); /* Gecko (Firefox, ...) */ 7 | background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#ddd)); /* Webkit (Chrome, Safari, ...) */ 8 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#f8f8f8', endColorstr='#dddddd'); /* IE 5.5 - 7 */ 9 | -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f8f8f8', endColorstr='#dddddd'); /* IE 8 */ 10 | 11 | border-color: #bbb; 12 | border-style: solid; 13 | border-width: 1px 1px 1px 0; 14 | 15 | color: #333; 16 | display: block; 17 | font-size: 11px; 18 | font-weight: bold; 19 | line-height: 21px; 20 | margin: 0; 21 | padding: 0 10px 0 11px; 22 | text-decoration: none; 23 | text-shadow: 1px 1px 0 #fff; 24 | position: relative; /* to please IE */ 25 | } 26 | 27 | #clipboard_button.active 28 | { 29 | background-color: #bbb; 30 | background: url('../images/button_selected.svg') 0 0 no-repeat; /* Opera needs an "image" :( - using svg for this so it will scale properly without looking too ugly */ 31 | background: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#aaa)); /* Konquerer */ 32 | background: -moz-linear-gradient(top, #ccc, #aaa); /* Gecko (Firefox, ...) */ 33 | background: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#aaa)); /* Webkit (Chrome, Safari, ...) */ 34 | filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#aaaaaa'); /* IE 5.5 - IE 7 */ 35 | -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#aaaaaa'); /* IE 8 */ 36 | 37 | color: #000; 38 | text-shadow: 1px 1px 0 rgba(255,255,255,0.4); 39 | border-color: #bbb; 40 | } 41 | 42 | 43 | 44 | 45 | #clipboard_container 46 | { 47 | position: relative; 48 | float: left; 49 | margin-left: 5px; 50 | } 51 | 52 | #clipboard_button 53 | { 54 | height: 21px; 55 | width: 23px; 56 | padding: 0; 57 | border-width: 1px; 58 | text-align: center; 59 | 60 | border-radius: 5px; /* Standard, Opera 10, IE 9 */ 61 | -khtml-border-radius: 3px; /* Konquerer */ 62 | -moz-border-radius: 3px ; /* Gecko (Firefox, ...) */ 63 | -webkit-border-radius: 3px; /* Webkit (Chrome, Safari, ...) */ 64 | /* IE <= 9 not supported */ 65 | } 66 | 67 | #clipboard_button img 68 | { 69 | padding-top: 2px; 70 | } 71 | -------------------------------------------------------------------------------- /app/models/deployment_credential.rb: -------------------------------------------------------------------------------- 1 | class DeploymentCredential < ActiveRecord::Base 2 | STATUS_ACTIVE = 1 3 | STATUS_INACTIVE = 0 4 | 5 | belongs_to :repository 6 | belongs_to :gitolite_public_key 7 | belongs_to :user 8 | 9 | attr_accessible :perm 10 | validates_presence_of :perm, :user, :repository 11 | validates_presence_of :gitolite_public_key 12 | validate :correct_key_type, :correct_perm_type, :no_duplicate_creds 13 | validate :owner_matches_key 14 | 15 | named_scope :active, {:conditions => {:active => STATUS_ACTIVE}} 16 | named_scope :inactive, {:conditions => {:active => STATUS_INACTIVE}} 17 | 18 | def perm= (value) 19 | write_attribute(:perm, (value.upcase rescue nil)) 20 | end 21 | 22 | # Provide a role-like interface. 23 | # Support :commit_access and :view_changesets 24 | @@equivalence = nil 25 | def allowed_to?( cred ) 26 | @@equivalence ||= { 27 | :view_changesets => ["R","RW+"], 28 | :commit_access => ["RW+"] 29 | } 30 | return false unless honored? 31 | 32 | # Deployment Credentials equivalence matrix 33 | return false unless @@equivalence[cred] && @@equivalence[cred].index(perm) 34 | true 35 | end 36 | 37 | # Deployment Credentials ignored unless created by someone who still has permission to create them 38 | def honored? 39 | user.admin? || user.allowed_to?(:create_deployment_keys,repository.project) 40 | end 41 | 42 | def self.valid_perms 43 | ["R", "RW+"] 44 | end 45 | 46 | def self.default_perm 47 | "RW+" 48 | end 49 | 50 | def to_s 51 | return File.join("Deploy Key: #{repository.identifier}-#{gitolite_public_key.identifier}: #{mode.to_s}") 52 | end 53 | 54 | protected 55 | 56 | def correct_key_type 57 | if gitolite_public_key && gitolite_public_key.key_type != GitolitePublicKey::KEY_TYPE_DEPLOY 58 | errors.add_to_base("Public Key Must Be a Deployment Key") 59 | end 60 | end 61 | 62 | def correct_perm_type 63 | if !self.class.valid_perms.index(perm) 64 | errors.add(:perm, "must be one of #{self.class.valid_perms.join(',')}") 65 | end 66 | end 67 | 68 | def owner_matches_key 69 | return if user.nil? || gitolite_public_key.nil? 70 | if user != gitolite_public_key.user 71 | errors.add_to_base("Credential owner cannot be different than owner of Key.") 72 | end 73 | end 74 | 75 | def no_duplicate_creds 76 | return if !new_record? || repository.nil? || gitolite_public_key.nil? 77 | repository.deployment_credentials.each do |cred| 78 | if cred.gitolite_public_key == gitolite_public_key 79 | errors.add_to_base("This Public Key has already been used in a Deployment Credential for this repository.") 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /db/migrate/2011081300000_create_git_repository_extras.rb: -------------------------------------------------------------------------------- 1 | class CreateGitRepositoryExtras < ActiveRecord::Migration 2 | def self.up 3 | 4 | drop_table :git_repository_extras if self.table_exists?("git_repository_extras") 5 | 6 | create_table :git_repository_extras do |t| 7 | t.column :repository_id, :integer 8 | # from repository extra columns 9 | t.column :git_daemon, :integer, :default =>1 10 | t.column :git_http, :integer, :default=>1 11 | t.column :notify_cia, :integer, :default=>0 12 | # from Hooks Keys table 13 | t.column :key, :string 14 | end 15 | 16 | 17 | GitHostingObserver.set_update_active(false) 18 | Project.find(:all).each do |project| 19 | if project.repository.is_a?(Repository::Git) 20 | 21 | #create extra object 22 | e = GitRepositoryExtra.new() 23 | begin 24 | e.git_daemon = project.repository.git_daemon || 1 25 | e.git_http = project.repository.git_http || 1 26 | e.key = project.repository.hook_key.key 27 | rescue 28 | e.git_daemon = 1 29 | e.git_http = 1 30 | end 31 | e.repository_id = project.repository.id 32 | e.save 33 | 34 | #update repo url to match location of gitolite repos 35 | r = project.repository 36 | repo_name= project.parent ? File.join(GitHosting::get_full_parent_path(project, true),project.identifier) : project.identifier 37 | r.url = File.join(Setting.plugin_redmine_git_hosting['gitRepositoryBasePath'], "#{repo_name}.git") 38 | r.root_url = r.url 39 | r.extra = e 40 | r.save 41 | 42 | end 43 | end 44 | 45 | # this next part requires running commands as git user 46 | # use a begin/rescue block because this could easily bomb out 47 | # if settings aren't correct to begin with 48 | begin 49 | %x[ rm -rf '#{ GitHosting.get_tmp_dir }' ] 50 | GitHosting.setup_hooks 51 | GitHostingObserver.set_update_active(false) 52 | rescue 53 | end 54 | 55 | # even if git commands above didn't work properly, attempt to 56 | # eliminate tmp dir in case they partially worked, and we have 57 | # residual crap belonging to wrong user 58 | begin 59 | %x[ rm -rf '#{ GitHosting.get_tmp_dir }' ] 60 | rescue 61 | end 62 | 63 | 64 | if self.table_exists?("git_hook_keys") 65 | drop_table :git_hook_keys 66 | end 67 | if self.column_exists?(:repositories, :git_daemon) 68 | remove_column :repositories, :git_daemon 69 | end 70 | if self.column_exists?(:repositories, :git_http) 71 | remove_column :repositories, :git_http 72 | end 73 | 74 | end 75 | 76 | def self.down 77 | drop_table :git_repository_extras 78 | end 79 | 80 | def self.table_exists?(name) 81 | ActiveRecord::Base.connection.tables.include?(name) 82 | end 83 | def self.column_exists?(table_name, column_name) 84 | columns(table_name).any?{ |c| c.name == column_name.to_s } 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /app/models/cia_notification_mailer.rb: -------------------------------------------------------------------------------- 1 | require "xmlrpc/client" 2 | 3 | class CiaNotificationMailer < ActionMailer::Base 4 | 5 | helper :git_hosting 6 | include GitHostingHelper 7 | 8 | def notification(revision, branch) 9 | @subject = "DeliverXML" 10 | @recipients = ["cia@cia.vc"] 11 | #@recipients = ["ufs@ufsoft.org"] 12 | @content_type = "text/xml" 13 | from "CIABOT-NOREPLY@#{ Setting['host_name'].match('localhost')? Setting['mail_from'].split('@')[-1] : Setting['host_name'] }" 14 | @sent_on = Time.now 15 | @body = render_message( 16 | "cia_notification.erb", :revision => revision, :branch => branch, 17 | :plugin => Redmine::Plugin.find('redmine_git_hosting') 18 | ) 19 | GitHosting.logger.debug "---8<----8<--- CIA Notification Body ---8<----8<---\n#{body}---8<----8<--- CIA Notification Body ---8<----8<---" 20 | @headers = { 21 | "Message-ID" => "<#{revision.revision}.#{revision.author}@#{revision.project.name}>" 22 | } 23 | end 24 | 25 | 26 | # Overrides default deliver! method to first try to deliver the CIA notification 27 | # through RPC(3 seconds timeout). If failed, send it by email. 28 | def deliver!(mail = @mail) 29 | 30 | rpc_server = XMLRPC::Client.new( 31 | host="www.cia.vc", path="/RPC2", port=nil, proxy_host=nil, 32 | proxy_port=nil, user=nil, password=nil, use_ssl=false, timeout=3 33 | ) 34 | 35 | begin 36 | ok, result = rpc_server.call2("hub.deliver", @body) 37 | if ok: 38 | GitHosting.logger.info "RPC Called. OK => #{ok} Result => #{result}" 39 | return false 40 | end 41 | GitHosting.logger.info "Failed to post the RPC call: #{result}" 42 | rescue XMLRPC::FaultException => e 43 | GitHosting.logger.info "RPC Failed. Error => #{e}" 44 | rescue Errno::ECONNREFUSED => e 45 | GitHosting.logger.info "RPC Connection Refused. Error => #{e}" 46 | rescue SocketError => e 47 | GitHosting.logger.info "RPC Socket Error. Error => #{e}" 48 | rescue Exception => e 49 | GitHosting.logger.info "RPC Error. Error => #{e}" 50 | end 51 | 52 | GitHosting.logger.info "Delivering By Email" 53 | 54 | return false if (recipients.nil? || recipients.empty?) && 55 | (cc.nil? || cc.empty?) && 56 | (bcc.nil? || bcc.empty?) 57 | 58 | # Log errors when raise_delivery_errors is set to false, Rails does not 59 | raise_errors = self.class.raise_delivery_errors 60 | self.class.raise_delivery_errors = true 61 | begin 62 | return super(mail) 63 | rescue Exception => e 64 | if raise_errors 65 | raise e 66 | elsif mylogger 67 | GitHosting.logger.error "The following error occured while sending email notification: \"#{e.message}\". Check your configuration in config/configuration.yml." 68 | end 69 | ensure 70 | self.class.raise_delivery_errors = raise_errors 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/roles_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'roles_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module RolesControllerPatch 9 | # Pre-1.4 (Not RESTfull) 10 | def new_with_disable_update 11 | # Turn of updates during repository update 12 | GitHostingObserver.set_update_active(false); 13 | 14 | # Do actual update 15 | new_without_disable_update 16 | 17 | # Reenable updates to perform a single update 18 | GitHostingObserver.set_update_active(true); 19 | end 20 | 21 | # Post-1.4 (RESTfull) 22 | def create_with_disable_update 23 | # Turn of updates during repository update 24 | GitHostingObserver.set_update_active(false); 25 | 26 | # Do actual update 27 | create_without_disable_update 28 | 29 | # Reenable updates to perform a single update 30 | GitHostingObserver.set_update_active(true); 31 | end 32 | 33 | # Pre-1.4 (Not RESTfull) 34 | def edit_with_disable_update 35 | # Turn of updates during repository update 36 | GitHostingObserver.set_update_active(false); 37 | 38 | # Do actual update 39 | edit_without_disable_update 40 | 41 | # Reenable updates to perform a single update 42 | GitHostingObserver.set_update_active(true); 43 | end 44 | 45 | # Post-1.4 (RESTfull) 46 | def update_with_disable_update 47 | # Turn of updates during repository update 48 | GitHostingObserver.set_update_active(false); 49 | 50 | # Do actual update 51 | update_without_disable_update 52 | 53 | # Reenable updates to perform a single update 54 | GitHostingObserver.set_update_active(true); 55 | end 56 | 57 | def destroy_with_disable_update 58 | # Turn of updates during repository update 59 | GitHostingObserver.set_update_active(false); 60 | 61 | # Do actual update 62 | destroy_without_disable_update 63 | 64 | # Reenable updates to perform a single update 65 | GitHostingObserver.set_update_active(true); 66 | end 67 | def self.included(base) 68 | base.class_eval do 69 | unloadable 70 | end 71 | begin 72 | # RESTfull (post-1.4) 73 | base.send(:alias_method_chain, :create, :disable_update) 74 | rescue 75 | # Not RESTfull (pre-1.4) 76 | base.send(:alias_method_chain, :new, :disable_update) rescue nil 77 | end 78 | begin 79 | # RESTfull (post-1.4) 80 | base.send(:alias_method_chain, :update, :disable_update) 81 | rescue 82 | # Not RESTfull (pre-1.4) 83 | base.send(:alias_method_chain, :edit, :disable_update) rescue nil 84 | end 85 | 86 | base.send(:alias_method_chain, :destroy, :disable_update) rescue nil 87 | end 88 | end 89 | end 90 | end 91 | 92 | # Patch in changes 93 | RolesController.send(:include, GitHosting::Patches::RolesControllerPatch) 94 | -------------------------------------------------------------------------------- /assets/javascripts/modalbox/lib/scriptaculous.js: -------------------------------------------------------------------------------- 1 | // script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008 2 | 3 | // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining 6 | // a copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be 14 | // included in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | // 24 | // For details, see the script.aculo.us web site: http://script.aculo.us/ 25 | 26 | var Scriptaculous = { 27 | Version: '1.8.1', 28 | require: function(libraryName) { 29 | // inserting via DOM fails in Safari 2.0, so brute force approach 30 | document.write(' 33 | <% end %> 34 | 35 | <% if (!User.current.anonymous?) || repository.extra[:git_http].to_s != "0" || (project.is_public && repository.extra[:git_daemon].to_s != "0" ) %> 36 |
37 |
38 |

Repository Access Links:

39 | 42 |
    43 | <% if !User.current.anonymous? %> 44 |
  • SSH
  • 45 | <% end %> 46 | <% if repository.extra[:git_http].to_s != "0" %> 47 |
  • HTTP
  • 48 | <% end %> 49 | <% if project.is_public && repository.extra[:git_daemon].to_s != "0" %> 50 |
  • Git
  • 51 | <% end %> 52 |
53 | 54 | This URL has Read-Only access. 55 |
56 |
57 | <% end %> 58 | <% end %> 59 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | # 4 | # These functions are for backward compatability with versions of redmine < 1.3 5 | # Only define these if not already defined (although load-order seems to load plugin 6 | # helpers before main ones, so check not necessary). 7 | # 8 | # 1/05/12 John Kubiatowicz 9 | # 4/01/12 Only define backward-compatible functions if TabularFormBuilder exists 10 | # (seems some versions of rails will go ahead and define these functions 11 | # and override properly defined versions). Note that Redmine 1.4+ removes 12 | # lib/tabular_form_builder.rb but defines these functions using new 13 | # builder functions... 14 | # 8/10/12 Added "labelled_fields_for", since that seems to be coming in 1.4 15 | if !defined?(labelled_form_for) && File.exists?(Rails.root.join("lib/tabular_form_builder.rb")) 16 | def labelled_form_for(*args, &proc) 17 | args << {} unless args.last.is_a?(Hash) 18 | options = args.last 19 | options.merge!({:builder => TabularFormBuilder,:lang => current_language}) 20 | form_for(*args, &proc) 21 | end 22 | end 23 | 24 | if !defined?(labelled_remote_form_for) && File.exists?(Rails.root.join("lib/tabular_form_builder.rb")) 25 | def labelled_remote_form_for(*args, &proc) 26 | args << {} unless args.last.is_a?(Hash) 27 | options = args.last 28 | options.merge!({:builder => TabularFormBuilder,:lang => current_language}) 29 | remote_form_for(*args, &proc) 30 | end 31 | end 32 | 33 | if !defined?(labelled_fields_for) && File.exists?(Rails.root.join("lib/tabular_form_builder.rb")) 34 | def labelled_fields_for(*args, &proc) 35 | args << {} unless args.last.is_a?(Hash) 36 | options = args.last 37 | options.merge!({:builder => TabularFormBuilder,:lang => current_language}) 38 | fields_for(*args, &proc) 39 | end 40 | end 41 | 42 | # Generic helper functions 43 | def reldir_add_dotslash(path) 44 | # Is this a relative path? 45 | stripped = (path || "").lstrip.rstrip 46 | norm = File.expand_path(stripped,"/") 47 | ((stripped[0,1] != "/")?".":"") + norm + ((norm[-1,1] != "/")?"/":"") 48 | end 49 | 50 | # Port-receive Mode 51 | def post_receive_mode(prurl) 52 | if prurl.active==0 53 | l(:label_inactive) 54 | elsif prurl.mode == :github 55 | l(:label_github_post) 56 | else 57 | l(:label_empty_get) 58 | end 59 | end 60 | 61 | # Refspec for mirrors 62 | def refspec(mirror, max_refspec=0) 63 | if mirror.push_mode==RepositoryMirror::PUSHMODE_MIRROR 64 | l(:all_references) 65 | else 66 | result=[] 67 | result << l(:all_branches) if mirror.include_all_branches 68 | result << l(:all_tags) if mirror.include_all_tags 69 | result << mirror.explicit_refspec if (max_refspec == 0) || ((1..max_refspec) === mirror.explicit_refspec.length) 70 | result << l(:explicit) if (max_refspec > 0) && (mirror.explicit_refspec.length > max_refspec) 71 | result.join(",
") 72 | end 73 | end 74 | 75 | # Mirror Mode 76 | def mirror_mode(mirror) 77 | if mirror.active==0 78 | l(:label_inactive) 79 | else 80 | [l(:label_mirror),l(:label_forced),l(:label_unforced)][mirror.push_mode] 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /app/views/projects/_git_urls.erb: -------------------------------------------------------------------------------- 1 | <% if project.repository && project.repository.is_a?(Repository::Git) %> 2 | <% content_for :header_tags do %> 3 | 4 | <%= stylesheet_link_tag('git_url_display', :plugin => 'redmine_git_hosting') %> 5 | <%= javascript_include_tag('git_url_display', :plugin => 'redmine_git_hosting') %> 6 | 7 | <%= stylesheet_link_tag('zero_clipboard', :plugin => 'redmine_git_hosting') %> 8 | <%= javascript_include_tag('ZeroClipboard', :plugin => 'redmine_git_hosting') %> 9 | <%= javascript_include_tag('zero_clipboard_setup', :plugin => 'redmine_git_hosting') %> 10 | 11 | 12 | 33 | <% end %> 34 | 35 | <% if (project.module_enabled?(:repository) && Setting.plugin_redmine_git_hosting['gitRepositoriesShowUrl'].to_s != "false" ) && ((!User.current.anonymous?) || project.repository.extra[:git_http].to_s != "0" || (project.is_public && project.repository.extra[:git_daemon].to_s != "0" )) %> 36 |
37 | <% if GitHosting.multi_repos? && project.repositories.count > 1 %> 38 |

Default Git Repository

39 | <% else %> 40 |

Git Repository

41 | <% end %> 42 |
43 | 46 |
    47 | <% if !User.current.anonymous? %> 48 |
  • SSH
  • 49 | <% end %> 50 | <% if project.repository.extra[:git_http].to_s != "0" %> 51 |
  • HTTP
  • 52 | <% end %> 53 | <% if project.is_public && project.repository.extra[:git_daemon].to_s != "0" %> 54 |
  • Git
  • 55 | <% end %> 56 |
57 | 58 | This URL has Read-Only access. 59 |
60 |
61 | <% end %> 62 | <% end %> 63 | -------------------------------------------------------------------------------- /app/views/repositories/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= error_messages_for 'repository' %> 2 | 3 | <% content_for :header_tags do %> 4 | <%= stylesheet_link_tag('application', :plugin => 'redmine_git_hosting') %> 5 | <%= stylesheet_link_tag('modalbox/modalbox', :plugin => 'redmine_git_hosting') %> 6 | <%= javascript_include_tag('modalbox/modalbox', :plugin => 'redmine_git_hosting') %> 7 | <% end %> 8 | 9 |
10 | <% if @repository && @repository.is_a?(Repository::Git) && (GitHostingHelper.can_view_deployment_keys(@repository.project) || GitHostingHelper.can_view_post_receive_urls(@repository.project) || GitHostingHelper.can_view_mirrors(@repository.project)) %> 11 |

Repository Options

12 | <% end %> 13 | 14 |

15 | <%= label_tag('repository_scm', l(:label_scm)) %><%= scm_select_tag(@repository) %> 16 | <% if @repository && ! @repository.class.scm_available %> 17 | <%= l(:text_scm_command_not_available) %> 18 | <% end %> 19 |

20 | 21 |

<%= f.check_box :is_default, :label => :field_repository_is_default %>

22 | <% if @repository && @repository.is_a?(Repository::Git) %> 23 |

<%= f.text_field :identifier, :label => (@repository.new_record? ? :field_identifier : :label_identifier_cannot_be_changed), :disabled => !@repository.new_record? %>

24 | <% else %> 25 |

<%= f.text_field :identifier %>

26 | <% end %> 27 | 28 | <% button_disabled = true %> 29 | <% if @repository %> 30 | <% button_disabled = ! @repository.class.scm_available %> 31 | <% end %> 32 | 33 | <% if @repository && @repository.is_a?(Repository::Git) %> 34 |

35 | <%= label_tag "extra[git_daemon]", l(:field_git_daemon) %> 36 | <%= select_tag "extra[git_daemon]", options_for_select([ [l(:label_disabled), "0"], [l(:label_enabled), "1"]], :selected=>(@project.is_public ? @repository.extra[:git_daemon].to_s : "0")), :disabled => !@project.is_public %> 37 |

38 |

39 | <%= label_tag "extra[git_http]", l(:field_git_http) %> 40 | <%= select_tag "extra[git_http]", options_for_select([ [l(:label_disabled), "0"], [l(:label_https_only), "1"], [l(:label_https_and_http), "2"] ], :selected=>@repository.extra[:git_http].to_s) %> 41 |

42 |

43 | <%= label_tag "extra[notify_cia]", l(:field_notify_cia) %> 44 | <%= select_tag "extra[notify_cia]", options_for_select([ [l(:label_disabled), "0"], [l(:label_enabled), "1"]], :selected=>@repository.extra[:notify_cia].to_s) %> 45 | <% if @repository.extra[:notify_cia].to_s == "1" %> 46 | "test", :projectid => @repository.project.identifier) %>"><%= l(:field_notify_cia_test) %> 47 | 48 | <% end %> 49 |

50 | <%= javascript_include_tag('notify_cia_test', :plugin => 'redmine_git_hosting') %> 51 |
52 | <% else %> 53 | <%= repository_field_tags(f, @repository) if @repository %> 54 | <% end %> 55 | 56 | <%= submit_tag(@repository.new_record? ? l(:button_create) : l(:button_save), :disabled => button_disabled) %> 57 | <%= link_to l(:button_cancel), settings_project_path(@project, :tab => 'repositories') %> 58 |
59 | -------------------------------------------------------------------------------- /selinux/redmine_git.te: -------------------------------------------------------------------------------- 1 | policy_module(redmine_git,1.0.0) 2 | 3 | ######################################## 4 | # 5 | # Declarations 6 | # 7 | require { 8 | type httpd_t, httpd_sys_script_t, httpd_sys_script_exec_t; 9 | type sudo_db_t; 10 | type httpd_redmine_git_script_t; 11 | type httpd_redmine_git_script_exec_t; 12 | type gitosis_var_lib_t; 13 | class process { setrlimit setfscreate }; 14 | class netlink_route_socket { write getattr read bind create nlmsg_read }; 15 | class capability { setuid sys_resource setgid }; 16 | class dir { getattr search write write rename create reparent rmdir }; 17 | class lnk_file unlink; 18 | } 19 | 20 | apache_content_template(redmine_git) 21 | 22 | permissive httpd_redmine_git_script_t; 23 | 24 | ######################################## 25 | # 26 | # httpd_redmine_git_script local policy 27 | # 28 | ######################################## 29 | 30 | manage_dirs_pattern(httpd_redmine_git_script_t, httpd_redmine_git_script_rw_t, httpd_redmine_git_script_rw_t) 31 | manage_files_pattern(httpd_redmine_git_script_t, httpd_redmine_git_script_rw_t, httpd_redmine_git_script_rw_t) 32 | 33 | domain_use_interactive_fds(httpd_redmine_git_script_t) 34 | 35 | files_read_etc_files(httpd_redmine_git_script_t) 36 | 37 | miscfiles_read_localization(httpd_redmine_git_script_t) 38 | 39 | # Allow our scripts to be called by redmine/apache 40 | httpd_redmine_git_script_domtrans(httpd_sys_script_t) 41 | 42 | # Allow us to access to rest of redmine site 43 | miscfiles_read_public_files(httpd_redmine_git_script_t) 44 | miscfiles_manage_public_files(httpd_redmine_git_script_t) 45 | 46 | #============= httpd_redmine_git_script_t ============== 47 | #Specific capabilities identified by audit2allow 48 | 49 | allow httpd_redmine_git_script_t self:capability audit_write; 50 | allow httpd_redmine_git_script_t self:capability { setuid sys_resource setgid }; 51 | allow httpd_redmine_git_script_t self:key write; 52 | allow httpd_redmine_git_script_t self:netlink_audit_socket { write nlmsg_relay create read }; 53 | allow httpd_redmine_git_script_t self:netlink_route_socket { write getattr read bind create nlmsg_read }; 54 | allow httpd_redmine_git_script_t self:process setrlimit; 55 | allow httpd_redmine_git_script_t sudo_db_t:dir { getattr search }; 56 | 57 | # Capabilities required to manage gitolite repositories 58 | allow httpd_redmine_git_script_t gitosis_var_lib_t:dir { rename create reparent rmdir }; 59 | allow httpd_redmine_git_script_t gitosis_var_lib_t:lnk_file unlink; 60 | gitosis_read_lib_files(httpd_redmine_git_script_t) 61 | gitosis_manage_lib_files(httpd_redmine_git_script_t) 62 | 63 | apache_rw_stream_sockets(httpd_redmine_git_script_t) 64 | kernel_read_kernel_sysctls(httpd_redmine_git_script_t) 65 | logging_send_syslog_msg(httpd_redmine_git_script_t) 66 | 67 | # These seem to be needed for ssh.... Not sure why ssh needs 68 | # to read and/or validate contexts... 69 | allow httpd_redmine_git_script_t self:process setfscreate; 70 | miscfiles_manage_cert_dirs(httpd_redmine_git_script_t) 71 | miscfiles_manage_cert_files(httpd_redmine_git_script_t) 72 | selinux_load_policy(httpd_redmine_git_script_t) 73 | selinux_validate_context(httpd_redmine_git_script_t) 74 | seutil_read_file_contexts(httpd_redmine_git_script_t) 75 | seutil_search_default_contexts(httpd_redmine_git_script_t) 76 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | def install_redmine_git_hosting_routes(map) 2 | # URL for items of type httpServer/XXX.git. Some versions of rails has problems with multiple regex expressions, so avoid... 3 | # Note that 'http_server_subdir' is either empty (default case) or ends in '/'. 4 | map.connect ":repo_path/*path", 5 | :prefix => (Setting.plugin_redmine_git_hosting['httpServerSubdir'] rescue ""), :repo_path => /([^\/]+\/)*?[^\/]+\.git/, :controller => 'git_http' 6 | 7 | # Handle the public keys plugin to my/account. 8 | map.resources :public_keys, :controller => 'gitolite_public_keys', :path_prefix => 'my' 9 | map.connect 'my/account/public_key/:public_key_id', :controller => 'my', :action => 'account' 10 | map.connect 'users/:id/edit/public_key/:public_key_id', :controller => 'users', :action => 'edit', :conditions => {:method => [:get]} 11 | 12 | # Handle hooks and mirrors 13 | map.connect 'githooks', :controller => 'gitolite_hooks', :action => 'stub' 14 | map.connect 'githooks/post-receive', :controller => 'gitolite_hooks', :action => 'post_receive' 15 | map.connect 'githooks/test', :controller => 'gitolite_hooks', :action => 'test' 16 | map.with_options :controller => 'repositories' do |repo_mapper| 17 | repo_mapper.with_options :controller => 'repository_mirrors' do |mirror_views| 18 | mirror_views.connect 'repositories/:repository_id/mirrors/new', :action => 'create', :conditions => {:method => [:get, :post]} 19 | mirror_views.connect 'repositories/:repository_id/mirrors/edit/:id', :action => 'edit' 20 | mirror_views.connect 'repositories/:repository_id/mirrors/push/:id', :action => 'push' 21 | mirror_views.connect 'repositories/:repository_id/mirrors/update/:id', :action => 'update', :conditions => {:method => :post} 22 | mirror_views.connect 'repositories/:repository_id/mirrors/delete/:id', :action => 'destroy', :conditions => {:method => [:get, :delete]} 23 | end 24 | repo_mapper.with_options :controller => 'repository_post_receive_urls' do |post_receive_views| 25 | post_receive_views.connect 'repositories/:repository_id/post-receive-urls/new', :action => 'create', :conditions => {:method => [:get, :post]} 26 | post_receive_views.connect 'repositories/:repository_id/post-receive-urls/edit/:id', :action => 'edit' 27 | post_receive_views.connect 'repositories/:repository_id/post-receive-urls/update/:id', :action => 'update', :conditions => {:method => :post} 28 | post_receive_views.connect 'repositories/:repository_id/post-receive-urls/delete/:id', :action => 'destroy', :conditions => {:method => [:get, :delete]} 29 | end 30 | repo_mapper.with_options :controller => 'deployment_credentials' do |deploy_views| 31 | deploy_views.connect 'repositories/:repository_id/deployment-credentials/new', :action => 'create_with_key', :conditions => {:method => [:get, :post]} 32 | deploy_views.connect 'repositories/:repository_id/deployment-credentials/edit/:id', :action => 'edit' 33 | deploy_views.connect 'repositories/:repository_id/deployment-credentials/update/:id', :action => 'update', :conditions => {:method => :post} 34 | deploy_views.connect 'repositories/:repository_id/deployment-credentials/delete/:id', :action => 'destroy', :conditions => {:method => [:get, :delete]} 35 | end 36 | end 37 | end 38 | 39 | if defined? map 40 | install_redmine_git_hosting_routes(map) 41 | else 42 | ActionController::Routing::Routes.draw do |map| 43 | install_redmine_git_hosting_routes(map) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /app/models/git_hosting_observer.rb: -------------------------------------------------------------------------------- 1 | class GitHostingObserver < ActiveRecord::Observer 2 | observe :project, :user, :gitolite_public_key, :member, :role, :repository 3 | 4 | @@updating_active = true 5 | @@updating_active_stack = 0 6 | @@updating_active_flags = {} 7 | @@cached_project_updates = [] 8 | 9 | def reload_this_observer 10 | observed_classes.each do |klass| 11 | klass.name.constantize.add_observer(self) 12 | end 13 | end 14 | 15 | 16 | def self.set_update_active(*is_active) 17 | if !is_active || !is_active.first 18 | @@updating_active_stack += 1 19 | else 20 | is_active.each do |item| 21 | case item 22 | when Symbol then @@updating_active_flags[item] = true 23 | when Hash then @@updating_active_flags.merge!(item) 24 | when Project then @@cached_project_updates |= [item] 25 | end 26 | end 27 | 28 | # If about to transition to zero and have something to run, do it 29 | if @@updating_active_stack == 1 && (@@cached_project_updates.length > 0 || !@@updating_active_flags.empty?) 30 | @@cached_project_updates = @@cached_project_updates.flatten.uniq.compact 31 | GitHosting::update_repositories(@@cached_project_updates, @@updating_active_flags) 32 | @@cached_project_updates = [] 33 | @@updating_active_flags = {} 34 | end 35 | 36 | # Wait until after running update_repositories before releasing 37 | @@updating_active_stack -= 1 38 | if @@updating_active_stack < 0 39 | @@updating_active_stack = 0 40 | end 41 | end 42 | @@updating_active = (@@updating_active_stack == 0) 43 | end 44 | 45 | # Register args for updating and then do it without allowing recursive calls 46 | def self.bracketed_update_repositories(*args) 47 | set_update_active(false) 48 | set_update_active(*args) 49 | end 50 | 51 | def after_create(object) 52 | if not object.is_a?(Project) 53 | update_repositories(object) 54 | end 55 | end 56 | 57 | 58 | def before_save(object) 59 | if object.is_a?(Repository::Git) 60 | GitHosting.logger.debug "On GitHostingObserver.before_save for Repository::Git" 61 | end 62 | end 63 | 64 | 65 | def after_save(object) 66 | update_repositories(object) 67 | end 68 | 69 | 70 | def after_destroy(object) 71 | if object.is_a?(Repository::Git) 72 | update_repositories(object,:delete=>true) 73 | CachedShellRedirector::clear_cache_for_repository(object) 74 | else 75 | update_repositories(object) 76 | end 77 | end 78 | 79 | 80 | protected 81 | 82 | def update_repositories(object,*flags) 83 | projects = [] 84 | case object 85 | when Repository::Git then projects.push(object.project) 86 | when User then projects = object.projects unless is_login_save?(object) 87 | when GitolitePublicKey then projects = object.user.projects 88 | when Member then projects.push(object.project) 89 | when Role then projects = object.members.map(&:project).flatten.uniq.compact 90 | end 91 | if (projects.length > 0) 92 | if (@@updating_active) 93 | GitHosting::update_repositories(projects,*flags) 94 | else 95 | @@cached_project_updates.concat(projects) 96 | @@updating_active_flags.merge!(*flags) unless flags.empty? 97 | end 98 | end 99 | end 100 | 101 | 102 | # Test for the fingerprint of changes to the user model when the User actually logs in. 103 | def is_login_save?(user) 104 | user.changed? && user.changed.length == 2 && user.changed.include?("updated_on") && user.changed.include?("last_login_on") 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /selinux/README: -------------------------------------------------------------------------------- 1 | This directory contains a selinux policy crafted to cover the sudo and 2 | ssh scripts for the redmine_git_hosting plugin. In it, we define a new 3 | httpd-compatible type, "httpd_redmine_git_script_exec_t" which can be 4 | placed on the redmine_git_hosting/bin directory to allow sudo access 5 | from redmine code. The basic assumption is that scripts placed into 6 | this directory will be called from context "httpd_script_t" (i.e 7 | redmine). 8 | 9 | Once this plugin is placed under selinux control, three of the 10 | redmine_git_hosting settings can no longer be modified from the 11 | settings page. They are: 'gitUser', 'gitoliteIdentityFile', and 12 | 'gitoliteIdentityPublicKeyFile'. The plugin settings page will make 13 | this clear. The simplest way to modify these options is to 14 | temporarily place your system into permissive mode, refresh the 15 | setting page, change options, then place your system back into 16 | enforcing mode. Alternatively, you can alter the init.rb file and 17 | reinstall the plugin. Under normal operation, you will get one 18 | selinux complaint about /bin/touch in your log each time that you 19 | visit the plugin settings page. 20 | 21 | ******************* INSTALLATION AND SETUP ************************* 22 | Note that the redmine_git_hosting/bin directory must be constructed 23 | statically so that it can be labeled. You can do this with a series 24 | of rake tasks at the top-level of the redmine directory (after fixing 25 | up the defaults in the redmine_git_hosting init.rb file): 26 | 27 | # Build bin directory with customized scripts for redmine_git_hosting, 28 | # install new selinux policy, and install complete selinux context for 29 | # redmine+this plugin: 30 | 31 | rake selinux:install RAILS_ENV=production 32 | 33 | Since redmine doesn't currently have a selinux install option, this 34 | installation command is only available for this plugin. What this 35 | will do is label the whole redmine site with "public_content_rw_t", 36 | with the exception of the "dispatch*" files in public (set to 37 | "httpd_script_exec_t") and the scripts in redmine_git_hosting/bin (set 38 | to "httpd_redmine_git_script_exec_t"). 39 | 40 | If you happen to have multiple redmine installations, you can use a 41 | regular expression to describe the redmine root directories (this will 42 | translate into file context descriptions). For instance, if you have 43 | multiple redmine installations in directories whose paths start with 44 | "/source" and end with "redmine" you can use (notice the use of 45 | double-quotes!): 46 | 47 | rake selinux:install RAILS_ENV=production ROOT_PATTERN="/source/.*/redmine" 48 | 49 | Somewhat less far-reaching options include: 50 | 51 | # Build bin directory with customized scripts for redmine_git_hosting, 52 | # install new selinux policy, and install selinux context for 53 | # the redmine_git_hosting plugin 54 | 55 | rake selinux:redmine_git_hosting:install RAILS_ENV=production 56 | 57 | For those who are hand-crafting their own file context: 58 | 59 | # Build bin directory with customized scripts for redmine_git_hosting 60 | # and install new selinux policy. No file contexts will be 61 | # installed (so that you must do customization afterwards). 62 | 63 | rake selinux:redmine_git_hosting:install_scripts_and_policy RAILS_ENV=production 64 | 65 | Finally, to rebuild the policy file (redmine_git.pp) from source (redmine_git.te), 66 | you can type: 67 | 68 | # Rebuild redmine_git_hosting selinux policy from source 69 | 70 | rake selinux:redmine_git_hosting:build_policy RAILS_ENV=production 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /selinux/redmine_git.if: -------------------------------------------------------------------------------- 1 | 2 | ## policy for httpd_redmine_git_script 3 | 4 | 5 | ######################################## 6 | ## 7 | ## Execute a domain transition to run httpd_redmine_git_script. 8 | ## 9 | ## 10 | ## 11 | ## Domain allowed access. 12 | ## 13 | ## 14 | # 15 | interface(`httpd_redmine_git_script_domtrans',` 16 | gen_require(` 17 | type httpd_redmine_git_script_t, httpd_redmine_git_script_exec_t; 18 | ') 19 | 20 | domtrans_pattern($1, httpd_redmine_git_script_exec_t, httpd_redmine_git_script_t) 21 | ') 22 | 23 | 24 | ######################################## 25 | ## 26 | ## Search httpd_redmine_git_script rw directories. 27 | ## 28 | ## 29 | ## 30 | ## Domain allowed access. 31 | ## 32 | ## 33 | # 34 | interface(`httpd_redmine_git_script_search_rw_dir',` 35 | gen_require(` 36 | type httpd_redmine_git_script_rw_t; 37 | ') 38 | 39 | allow $1 httpd_redmine_git_script_rw_t:dir search_dir_perms; 40 | files_search_rw($1) 41 | ') 42 | 43 | ######################################## 44 | ## 45 | ## Read httpd_redmine_git_script rw files. 46 | ## 47 | ## 48 | ## 49 | ## Domain allowed access. 50 | ## 51 | ## 52 | # 53 | interface(`httpd_redmine_git_script_read_rw_files',` 54 | gen_require(` 55 | type httpd_redmine_git_script_rw_t; 56 | ') 57 | 58 | allow $1 httpd_redmine_git_script_rw_t:file r_file_perms; 59 | allow $1 httpd_redmine_git_script_rw_t:dir list_dir_perms; 60 | files_search_rw($1) 61 | ') 62 | 63 | ######################################## 64 | ## 65 | ## Create, read, write, and delete 66 | ## httpd_redmine_git_script rw files. 67 | ## 68 | ## 69 | ## 70 | ## Domain allowed access. 71 | ## 72 | ## 73 | # 74 | interface(`httpd_redmine_git_script_manage_rw_files',` 75 | gen_require(` 76 | type httpd_redmine_git_script_rw_t; 77 | ') 78 | 79 | manage_files_pattern($1, httpd_redmine_git_script_rw_t, httpd_redmine_git_script_rw_t) 80 | ') 81 | 82 | ######################################## 83 | ## 84 | ## Create, read, write, and delete 85 | ## httpd_redmine_git_script rw dirs. 86 | ## 87 | ## 88 | ## 89 | ## Domain allowed access. 90 | ## 91 | ## 92 | # 93 | interface(`httpd_redmine_git_script_manage_rw_dirs',` 94 | gen_require(` 95 | type httpd_redmine_git_script_rw_t; 96 | ') 97 | 98 | manage_dirs_pattern($1, httpd_redmine_git_script_rw_t, httpd_redmine_git_script_rw_t) 99 | ') 100 | 101 | 102 | ######################################## 103 | ## 104 | ## All of the rules required to administrate 105 | ## an httpd_redmine_git_script environment 106 | ## 107 | ## 108 | ## 109 | ## Domain allowed access. 110 | ## 111 | ## 112 | ## 113 | ## 114 | ## Role allowed access. 115 | ## 116 | ## 117 | ## 118 | # 119 | interface(`httpd_redmine_git_script_admin',` 120 | gen_require(` 121 | type httpd_redmine_git_script_t; 122 | type httpd_redmine_git_script_rw_t; 123 | ') 124 | 125 | allow $1 httpd_redmine_git_script_t:process { ptrace signal_perms }; 126 | ps_process_pattern($1, httpd_redmine_git_script_t) 127 | 128 | files_search_etc($1) 129 | admin_pattern($1, httpd_redmine_git_script_rw_t) 130 | 131 | ') 132 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/git_adapter_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'redmine/scm/adapters/git_adapter' 5 | 6 | module GitHosting 7 | module Patches 8 | module GitAdapterPatch 9 | 10 | def self.included(base) 11 | base.class_eval do 12 | unloadable 13 | end 14 | 15 | begin 16 | base.send(:alias_method_chain, :git_cmd, :sudo) 17 | rescue 18 | # Hm... might be pre-1.4, where :git_cmd => :scm_cmd 19 | base.send(:alias_method_chain, :scm_cmd, :sudo) rescue nil 20 | base.send(:alias_method, :git_cmd, :git_cmd_with_sudo) 21 | end 22 | 23 | base.extend(ClassMethods) 24 | base.class_eval do 25 | class << self 26 | begin 27 | alias_method_chain :sq_bin, :sudo 28 | begin 29 | alias_method_chain :client_command, :sudo 30 | rescue Exception =>e 31 | end 32 | rescue Exception => e 33 | # Hm.... Might be Redmine version < 1.2 (i.e. 1.1). Try redefining GIT_BIN. 34 | GitHosting.logger.warn "Seems to be early version of Redmine(1.1?), try redefining GIT_BIN." 35 | Redmine::Scm::Adapters::GitAdapter::GIT_BIN = GitHosting::git_exec() 36 | end 37 | end 38 | end 39 | end 40 | 41 | module ClassMethods 42 | def sq_bin_with_sudo 43 | return Redmine::Scm::Adapters::GitAdapter::shell_quote(GitHosting::git_exec()) 44 | end 45 | def client_command_with_sudo 46 | return GitHosting::git_exec() 47 | end 48 | end 49 | 50 | # Pre-1.4 command syntax 51 | def scm_cmd_with_sudo(*args, &block) 52 | git_cmd_with_sudo(args, &block) 53 | end 54 | 55 | # Post-1.4 command syntax 56 | def git_cmd_with_sudo(args, options = {}, &block) 57 | repo_path = root_url || url 58 | full_args = [GitHosting::git_exec(), '--git-dir', repo_path] 59 | if self.class.client_version_above?([1, 7, 2]) 60 | full_args << '-c' << 'core.quotepath=false' 61 | full_args << '-c' << 'log.decorate=no' 62 | end 63 | full_args += args 64 | 65 | cmd_str=full_args.map { |e| shell_quote e.to_s }.join(' ') 66 | 67 | # Compute string from repo_path that should be same as: repo.git_label(:assume_unique=>false) 68 | # If only we had access to the repo (we don't). 69 | repo_id=Repository.repo_path_to_git_label(repo_path) 70 | 71 | # Insert cache between shell execution and caller 72 | # repo_path argument used to identify cache entries 73 | CachedShellRedirector.execute(cmd_str,repo_id,options,&block) 74 | end 75 | 76 | # Check for latest commit (applied to this repo) and set it as a 77 | # limit for the oldest cached entries. This caused cached entries 78 | # to be ignored/invalidated if they are older than the latest log 79 | # entry 80 | def ignore_old_cache_entries 81 | Rails.logger.error "Running ignore_old_cache_entries" 82 | # Ask for latest "commit date" on all branches 83 | cmd_args = %w|log --all --date=iso --format=%cd -n 1 --date=iso| 84 | begin 85 | git_cmd(cmd_args,:uncached=>true) do |io| 86 | # Register this latest commit time as cache limit time 87 | limit=Time.parse(io.readline) 88 | CachedShellRedirector.limit_cache(root_url||url,limit) 89 | end 90 | rescue 91 | # Wasn't able to ask git for limit date. Just disable cache. 92 | CachedShellRedirector.clear_cache_for_repository(root_url||url) 93 | end 94 | end 95 | end 96 | end 97 | end 98 | 99 | # Patch in changes 100 | Redmine::Scm::Adapters::GitAdapter.send(:include, GitHosting::Patches::GitAdapterPatch) 101 | -------------------------------------------------------------------------------- /app/views/projects/settings/_repository.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :header_tags do %> 2 | <%= stylesheet_link_tag('application', :plugin => 'redmine_git_hosting') %> 3 | <%= stylesheet_link_tag('modalbox/modalbox', :plugin => 'redmine_git_hosting') %> 4 | <%= javascript_include_tag('modalbox/modalbox', :plugin => 'redmine_git_hosting') %> 5 | <% end %> 6 | 7 | <% labelled_remote_form_for :repository, @repository, :url => { :controller => 'repositories', :action => 'edit', :id => @project } do |f| %> 8 | <%= error_messages_for 'repository' %> 9 | 10 |
11 | <% if @repository && @repository.is_a?(Repository::Git) && (GitHostingHelper.can_view_deployment_keys(@repository.project) || GitHostingHelper.can_view_post_receive_urls(@repository.project) || GitHostingHelper.can_view_mirrors(@repository.project)) %> 12 |

Repository Options

13 | <% end %> 14 | 15 |

<%= label_tag('repository_scm', l(:label_scm)) %><%= scm_select_tag(@repository) %>

16 | 17 | <% if @repository && @repository.is_a?(Repository::Git) %> 18 |

19 | <%= label_tag "extra[git_daemon]", l(:field_git_daemon) %> 20 | <%= select_tag "extra[git_daemon]", options_for_select([ [l(:label_disabled), "0"], [l(:label_enabled), "1"]], :selected=>(@project.is_public ? @repository.extra[:git_daemon].to_s : "0")), :disabled => !@project.is_public %> 21 |

22 |

23 | <%= label_tag "extra[git_http]", l(:field_git_http) %> 24 | <%= select_tag "extra[git_http]", options_for_select([ [l(:label_disabled), "0"], [l(:label_https_only), "1"], [l(:label_https_and_http), "2"] ], :selected=>@repository.extra[:git_http].to_s) %> 25 |

26 |

27 | <%= label_tag "extra[notify_cia]", l(:field_notify_cia) %> 28 | <%= select_tag "extra[notify_cia]", options_for_select([ [l(:label_disabled), "0"], [l(:label_enabled), "1"]], :selected=>@repository.extra[:notify_cia].to_s) %> 29 | <% if @repository.extra[:notify_cia].to_s == "1" %> 30 | "test", :projectid => @repository.project.identifier) %>"><%= l(:field_notify_cia_test) %> 31 | 32 | <% end %> 33 |

34 | <%= javascript_include_tag('notify_cia_test', :plugin => 'redmine_git_hosting') %> 35 |
36 | <% else %> 37 | <%= repository_field_tags(f, @repository) if @repository %> 38 | <% end %> 39 |
40 | <% if @repository && !@repository.new_record? %> 41 | <%= link_to(l(:label_user_plural), {:controller => 'repositories', :action => 'committers', :id => @project}, :class => 'icon icon-user') %> 42 | <%= link_to(l(:button_delete), {:controller => 'repositories', :action => 'destroy', :id => @project}, :confirm => l(:text_are_you_sure),:method => :post,:class => 'icon icon-del') %> 43 | <% end %> 44 |
45 | <%= submit_tag((@repository.nil? || @repository.new_record?) ? l(:button_create) : l(:button_save), :disabled => @repository.nil?) %> 46 |
47 | <% end %> 48 | 49 | <% if @repository && @repository.is_a?(Repository::Git) && !(@repository.nil? || @repository.new_record?) %> 50 | <% if GitHostingHelper.can_view_deployment_keys(@repository.project) %> 51 | <%= render :partial => 'deployment_credentials/view_list' %> 52 | <% end %> 53 | 54 | <% if GitHostingHelper.can_view_post_receive_urls(@repository.project) %> 55 | <%= render :partial => 'repository_post_receive_urls/view_list' %> 56 | <% end %> 57 | 58 | <% if GitHostingHelper.can_view_mirrors(@repository.project) %> 59 | <%= render :partial => 'repository_mirrors/view_list' %> 60 | <% end %> 61 | <% end %> 62 | 63 | 64 | -------------------------------------------------------------------------------- /app/views/repository_post_receive_urls/_view_list.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 | <% if GitHostingHelper.can_create_post_receive_urls(@repository.project) %> 4 |
5 | <%= link_to("Add Post Receive URL", url_for(:controller => 'repository_post_receive_urls', :action => 'create', :repository_id => @repository.id), :class => 'icon icon-add add-post-receive-url' ) %> 6 |
7 | <% end %> 8 | 9 |

Post Receive URLs

10 | 11 | <% if @repository.repository_post_receive_urls.any? %> 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <% @repository.repository_post_receive_urls.each do |prurl| %> 23 | 24 | 25 | 26 | 32 | 33 | <% end %> 34 | 35 |
URLMode 
<%= link_to(h(prurl.url), url_for(:controller => 'repository_post_receive_urls', :action => 'edit', :repository_id => @repository.id, :id => prurl.id), :class => 'edit-post-receive-url') %><%= post_receive_mode prurl %>
<%= checked_image prurl.active? %>
27 | <% if GitHostingHelper.can_edit_post_receive_urls(@repository.project) %> 28 | <%= link_to(l(:button_edit), url_for(:controller => 'repository_post_receive_urls', :action => 'edit', :repository_id => @repository.id, :id => prurl.id), :class => 'icon icon-edit edit-post-receive-url') %> 29 | <%= link_to(l(:button_delete), url_for(:controller => 'repository_post_receive_urls', :action => 'destroy', :repository_id => @repository.id, :id => prurl.id), :class => 'icon icon-del delete-post-receive-url') %> 30 | <% end %> 31 |
36 |
37 | <% else %> 38 | 39 | 40 |
No Post Receive URLs Defined
41 | <% end %> 42 |
43 | 44 | 98 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/users_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'users_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module UsersControllerPatch 9 | def create_with_disable_update 10 | # Turn of updates during repository update 11 | GitHostingObserver.set_update_active(false); 12 | 13 | # Do actual update 14 | create_without_disable_update 15 | 16 | # Reenable updates to perform a single update 17 | GitHostingObserver.set_update_active(true); 18 | end 19 | def update_with_disable_update 20 | # Turn of updates during repository update 21 | GitHostingObserver.set_update_active(false); 22 | 23 | # Set public key values for view 24 | set_public_key_values 25 | 26 | # Do actual update 27 | update_without_disable_update 28 | 29 | # Reenable updates to perform a single update 30 | GitHostingObserver.set_update_active(true); 31 | end 32 | def destroy_with_disable_update 33 | # Turn of updates during repository update 34 | GitHostingObserver.set_update_active(false); 35 | 36 | # Do actual update 37 | destroy_without_disable_update 38 | 39 | # Reenable updates to perform a single update 40 | GitHostingObserver.set_update_active(:delete); 41 | end 42 | def edit_membership_with_disable_update 43 | # Turn of updates during repository update 44 | GitHostingObserver.set_update_active(false); 45 | 46 | # Do actual update 47 | edit_membership_without_disable_update 48 | 49 | # Reenable updates to perform a single update 50 | GitHostingObserver.set_update_active(true); 51 | end 52 | def destroy_membership_with_disable_update 53 | # Turn of updates during repository update 54 | GitHostingObserver.set_update_active(false); 55 | 56 | # Do actual update 57 | destroy_membership_without_disable_update 58 | 59 | # Reenable updates to perform a single update 60 | GitHostingObserver.set_update_active(true); 61 | end 62 | 63 | # Add in values for viewing public keys: 64 | def edit_with_public_keys 65 | # Set public key values for view 66 | set_public_key_values 67 | 68 | # Previous routine 69 | edit_without_public_keys 70 | end 71 | 72 | # Add in values for viewing public keys: 73 | def set_public_key_values 74 | @gitolite_user_keys = @user.gitolite_public_keys.active.user_key.find(:all,:order => 'title ASC, created_at ASC') 75 | @gitolite_deploy_keys = @user.gitolite_public_keys.active.deploy_key.find(:all,:order => 'title ASC, created_at ASC') 76 | @gitolite_public_keys = @gitolite_user_keys + @gitolite_deploy_keys 77 | @gitolite_public_key = @gitolite_public_keys.detect{|x| x.id == params[:public_key_id].to_i} 78 | if @gitolite_public_key.nil? 79 | if params[:public_key_id] 80 | # public_key specified that doesn't belong to @user. Kill off public_key_id and try again 81 | redirect_to :public_key_id => nil, :tab => nil 82 | return 83 | else 84 | @gitolite_public_key = GitolitePublicKey.new 85 | end 86 | end 87 | end 88 | 89 | def self.included(base) 90 | base.class_eval do 91 | unloadable 92 | 93 | helper :gitolite_public_keys 94 | end 95 | # Edit adds new functionality, so don't silently fail! 96 | base.send(:alias_method_chain, :edit, :public_keys) 97 | begin 98 | base.send(:alias_method_chain, :create, :disable_update) 99 | base.send(:alias_method_chain, :update, :disable_update) 100 | base.send(:alias_method_chain, :edit_membership, :disable_update) 101 | base.send(:alias_method_chain, :destroy_membership, :disable_update) 102 | # Put this last, since Redmine 1.1 doesn't have it.... 103 | base.send(:alias_method_chain, :destroy, :disable_update) 104 | rescue 105 | end 106 | end 107 | end 108 | end 109 | end 110 | 111 | # Patch in changes 112 | UsersController.send(:include, GitHosting::Patches::UsersControllerPatch) 113 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/members_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'members_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module MembersControllerPatch 9 | # pre-1.4 (Non RESTfull) 10 | def new_with_disable_update 11 | # Turn of updates during repository update 12 | GitHostingObserver.set_update_active(false); 13 | 14 | # Do actual update 15 | new_without_disable_update 16 | 17 | # Reenable updates to perform a single update 18 | GitHostingObserver.set_update_active(true); 19 | end 20 | # post-1.4 (RESTfull) 21 | def create_with_disable_update 22 | # Turn of updates during repository update 23 | GitHostingObserver.set_update_active(false); 24 | 25 | # Do actual update 26 | create_without_disable_update 27 | 28 | # Reenable updates to perform a single update 29 | GitHostingObserver.set_update_active(true); 30 | end 31 | # pre-1.4 (Non RESTfull) 32 | def edit_with_disable_update 33 | # Turn of updates during repository update 34 | GitHostingObserver.set_update_active(false); 35 | 36 | # Do actual update 37 | edit_without_disable_update 38 | 39 | # Reenable updates to perform a single update 40 | GitHostingObserver.set_update_active(true); 41 | end 42 | # post-1.4 (RESTfull) 43 | def update_with_disable_update 44 | # Turn of updates during repository update 45 | GitHostingObserver.set_update_active(false); 46 | 47 | # Do actual update 48 | update_without_disable_update 49 | 50 | # Reenable updates to perform a single update 51 | GitHostingObserver.set_update_active(true); 52 | end 53 | def destroy_with_disable_update 54 | # Turn of updates during repository update 55 | GitHostingObserver.set_update_active(false); 56 | 57 | # Do actual update 58 | destroy_without_disable_update 59 | 60 | # Reenable updates to perform a single update 61 | GitHostingObserver.set_update_active(:delete => true); 62 | end 63 | 64 | # Need to make sure that we can re-render the repository settings page 65 | # (Only for pre-1.4, i.e. single repo/project) 66 | def render_with_trigger_refresh(*options, &myblock) 67 | doing_update = options.detect {|x| x==:update || (x.is_a?(Hash) && x[:update])} 68 | if !doing_update 69 | render_without_trigger_refresh(*options, &myblock) 70 | else 71 | # For repository partial 72 | render_without_trigger_refresh *options do |page| 73 | yield page 74 | if (@repository ||= @project.repository) && (@repository.is_a?(Repository::Git)) 75 | page.replace_html "tab-content-repository", :partial => 'projects/settings/repository' 76 | end 77 | end 78 | end 79 | end 80 | 81 | def self.included(base) 82 | base.class_eval do 83 | unloadable 84 | 85 | helper :repositories 86 | end 87 | begin 88 | # RESTfull (post-1.4) 89 | base.send(:alias_method_chain, :create, :disable_update) 90 | rescue 91 | # Not RESTfull (pre-1.4) 92 | base.send(:alias_method_chain, :new, :disable_update) rescue nil 93 | end 94 | begin 95 | # RESTfull (post-1.4) 96 | base.send(:alias_method_chain, :update, :disable_update) 97 | rescue 98 | # Not RESTfull (pre-1.4) 99 | base.send(:alias_method_chain, :edit, :disable_update) rescue nil 100 | end 101 | base.send(:alias_method_chain, :destroy, :disable_update) rescue nil 102 | 103 | # This patch only needed when repository settings in same set 104 | # if tabs as members (i.e. pre-1.4, single repo) 105 | # (Note that patches not stabilized yet, so cannot just call: 106 | # Project.multi_repos? 107 | if !GitHosting.multi_repos? 108 | base.send(:alias_method_chain, :render, :trigger_refresh) rescue nil 109 | end 110 | end 111 | end 112 | end 113 | end 114 | 115 | # Patch in changes 116 | MembersController.send(:include, GitHosting::Patches::MembersControllerPatch) 117 | -------------------------------------------------------------------------------- /contrib/hooks/post-receive.redmine_gitolite.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'digest/sha1' 4 | require 'net/http' 5 | require 'net/https' 6 | require 'uri' 7 | 8 | STDOUT.sync=true 9 | $debug=false 10 | 11 | 12 | def log(msg, debug_only=false, with_newline=true) 13 | if $debug || (!debug_only) 14 | print msg + (with_newline ? "\n" : "") 15 | end 16 | end 17 | def get_git_repository_config(varname) 18 | (%x[git config #{varname} ]).chomp.strip 19 | end 20 | def get_http_params(rgh_vars) 21 | clear_time = Time.new.utc.to_i.to_s 22 | params = { "clear_time" => clear_time, "encoded_time" => Digest::SHA1.hexdigest(clear_time.to_s + rgh_vars["key"]) } 23 | rgh_vars.each_key do |v| 24 | if v != "key" 25 | params[v] = rgh_vars[v] 26 | end 27 | end 28 | params 29 | end 30 | 31 | # Need to do this ourselves, because 1.8.7 ruby is broken 32 | def set_form_data(request, params, sep = '&') 33 | request.body = params.map {|k,v| 34 | if v.instance_of?(Array) 35 | v.map {|e| "#{urlencode(k.to_s)}=#{urlencode(e.to_s)}"}.join(sep) 36 | else 37 | "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" 38 | end 39 | }.join(sep) 40 | 41 | request.content_type = 'application/x-www-form-urlencoded' 42 | end 43 | 44 | def urlencode(str) 45 | URI.encode(str,/[^a-zA-Z0-9_\.\-]/) 46 | end 47 | 48 | def run_query(url_str, params, with_https) 49 | url_str = (with_https ? "https://" : "http://" ) + url_str.gsub(/^http[s]*:\/\//, "") 50 | success = false 51 | begin 52 | url = URI.parse(url_str) 53 | http = Net::HTTP.new( url.host, url.port ) 54 | http.open_timeout = 20 55 | http.read_timeout = 180 56 | if with_https 57 | http.use_ssl = true 58 | http.verify_mode = OpenSSL::SSL::VERIFY_NONE 59 | end 60 | req = Net::HTTP::Post.new(url.request_uri) 61 | set_form_data(req,params) 62 | response = http.request(req) do |response| 63 | response.read_body do |body_frag| 64 | success = response.code.to_i == 200 ? true : false 65 | log(body_frag, false, false) 66 | end 67 | end 68 | #response = http.request(req) 69 | #puts response.header 70 | rescue Exception =>e 71 | #log("HTTP_ERROR:" + e.to_s, true, true) 72 | success = false 73 | end 74 | success 75 | end 76 | 77 | 78 | 79 | 80 | rgh_vars = {} 81 | rgh_var_names = [ "hooks.redmine_gitolite.key", "hooks.redmine_gitolite.url", "hooks.redmine_gitolite.projectid", "hooks.redmine_gitolite.repositoryid", "hooks.redmine_gitolite.debug", "hooks.redmine_gitolite.asynch"] 82 | rgh_var_names.each do |var_name| 83 | var_val = get_git_repository_config(var_name) 84 | if var_val.to_s == "" 85 | # Allow blank repositoryID (as default) 86 | if var_name != "hooks.redmine_gitolite.repositoryid" 87 | log("\n\nRepository does not have \"#{var_name}\" set. Skipping hook.\n\n", false, true) 88 | exit 89 | end 90 | else 91 | var_name = var_name.gsub(/^.*\./, "") 92 | rgh_vars[ var_name ] = var_val 93 | end 94 | end 95 | 96 | $debug = rgh_vars["debug"] == "true" 97 | 98 | 99 | # Let's read the refs passed to us 100 | refs = [] 101 | $<.each do |line| 102 | r = line.chomp.strip.split 103 | refs.push( [ r[0].to_s, r[1].to_s, r[2].to_s ].join(",") ) 104 | end 105 | rgh_vars["refs[]"] = refs 106 | 107 | if rgh_vars["asynch"] == "true" 108 | pid = fork 109 | exit unless pid.nil? 110 | pid = fork 111 | exit unless pid.nil? 112 | 113 | File.umask 0000 114 | 115 | STDIN.reopen '/dev/null' 116 | STDOUT.reopen '/dev/null', 'a' 117 | STDERR.reopen STDOUT 118 | 119 | end 120 | 121 | log("\n\n", false, true) 122 | log("Notifying ChiliProject/Redmine project #{rgh_vars['projectid']} about changes to this repo...", true, true) 123 | success = run_query(rgh_vars["url"], get_http_params(rgh_vars), true) 124 | if !success 125 | success = run_query(rgh_vars["url"], get_http_params(rgh_vars), false) 126 | end 127 | if(!success) 128 | log("Error contacting ChiliProject/Redmine about changes to this repo.", false, true) 129 | else 130 | log("Success", true, true) 131 | log("", true, true) 132 | end 133 | log("\n\n", false, true) 134 | 135 | exit 136 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/repositories_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'repositories_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module RepositoriesControllerPatch 9 | def show_with_git_instructions 10 | if @repository.is_a?(Repository::Git) and @repository.entries(@path, @rev).blank? 11 | # Fake list of repos 12 | @repositories = @project.all_repos 13 | render :action => 'git_instructions' 14 | else 15 | show_without_git_instructions 16 | end 17 | end 18 | 19 | # This patch is only for pre-1.4 Redmine (since they made this controller RESTful 20 | def edit_with_scm_settings 21 | GitHosting.logger.debug "On edit_with_scm_settings" 22 | 23 | # Turn off updates during repository update 24 | GitHostingObserver.set_update_active(false); 25 | params[:repository] ||= {} 26 | 27 | if params[:repository_scm] == "Git" && @project.repository 28 | params[:repository][:url] = GitHosting.repository_path(@project.repository) 29 | end 30 | 31 | if params[:repository_scm] == "Git" || @project.repository.is_a?(Repository::Git) 32 | #Evidently the ONLY way to update the repository.extra table is to basically copy/paste the existing controller code 33 | #the update line needs to go in the dead center of it. 34 | @repository = @project.repository 35 | if !@repository 36 | @repository = Repository.factory(params[:repository_scm]) 37 | @repository.project = @project if @repository 38 | end 39 | if request.post? && @repository 40 | @repository.attributes = params[:repository] 41 | if !params[:extra].nil? 42 | @repository.extra.update_attributes(params[:extra]) 43 | end 44 | @repository.save 45 | end 46 | 47 | 48 | render(:update) do |page| 49 | page.replace_html "tab-content-repository", :partial => 'projects/settings/repository' 50 | if @repository && !@project.repository 51 | @project.reload #needed to reload association 52 | page.replace_html "main-menu", render_main_menu(@project) 53 | end 54 | end 55 | 56 | if !@project.repository.nil? 57 | GitHostingObserver.bracketed_update_repositories(@project) 58 | end 59 | else 60 | edit_without_scm_settings 61 | end 62 | 63 | GitHostingObserver.set_update_active(true); 64 | end 65 | 66 | # Post-1.4, all creation is done by create (rather than edit) 67 | def create_with_scm_settings 68 | GitHostingObserver.set_update_active(false) 69 | 70 | # Must create repository first 71 | create_without_scm_settings 72 | 73 | if !@repository.errors.any? 74 | # Update repository extras 75 | if request.post? && @repository && !params[:extra].nil? 76 | @repository.extra.update_attributes(params[:extra]) 77 | end 78 | GitHostingObserver.set_update_active(@project) 79 | else 80 | GitHostingObserver.set_update_active(true) 81 | end 82 | end 83 | 84 | # Post-1.4, all of the updates are done by update (rather than edit with post) 85 | def update_with_scm_settings 86 | GitHostingObserver.set_update_active(false) 87 | 88 | update_without_scm_settings 89 | 90 | if !@repository.errors.any? 91 | # Update repository extras 92 | if request.put? && @repository && !params[:extra].nil? 93 | @repository.extra.update_attributes(params[:extra]) 94 | end 95 | GitHostingObserver.set_update_active(@project) 96 | else 97 | GitHostingObserver.set_update_active(true) 98 | end 99 | end 100 | 101 | def self.included(base) 102 | base.class_eval do 103 | unloadable 104 | end 105 | base.send(:alias_method_chain, :show, :git_instructions) 106 | 107 | # RESTful (post-1.4). 108 | base.send(:alias_method_chain, :create, :scm_settings) rescue nil 109 | 110 | begin 111 | # RESTfull (post-1.4) 112 | base.send(:alias_method_chain, :update, :scm_settings) 113 | rescue 114 | # Not RESTfull (pre-1.4) 115 | base.send(:alias_method_chain, :edit, :scm_settings) rescue nil 116 | end 117 | end 118 | end 119 | end 120 | end 121 | 122 | # Patch in changes 123 | RepositoriesController.send(:include, GitHosting::Patches::RepositoriesControllerPatch) 124 | -------------------------------------------------------------------------------- /tasks/redmine_git_hosting.rake: -------------------------------------------------------------------------------- 1 | # 2 | # Tasks in this namespace (redmine_git_hosting) are for administrative tasks 3 | # 4 | # TOP-LEVEL TARGETS: 5 | # 6 | # 1) Repopulate settings in the database with defaults from init.rb 7 | # 8 | # rake redmine_git_hosting:restore_defaults RAILS_ENV=xxx 9 | # 10 | # 2) Resynchronize/repair gitolite configuration (fix keys directory and configuration). 11 | # Also, expire repositories in the recycle_bin if necessary. 12 | # 13 | # rake redmine_git_hosting:update_repositories RAILS_ENV=xxx 14 | # 15 | # 3) Fetch all changesets for repositories and then rescynronize gitolite configuration (as in #1) 16 | # 17 | # rake redmine_git_hosting:fetch_changsets RAILS_ENV=xxx 18 | # 19 | # 4) Install custom scripts to the script directory. The optional argument 20 | # 'READ_ONLY=true' requests that the resulting scripts and script directory 21 | # be made read-only to the web server. The optional argument WEB_USER=xxx 22 | # states that scripts should be owned by user "xxx". If omitted, the 23 | # script attempts to figure out the web user by using "ps" and looking 24 | # for httpd. 25 | # 26 | # rake redmine_git_hosting:install_scripts [READ_ONLY=true] [WEB_USER=xxx] RAILS_ENV=yyy 27 | # 28 | # 5) Remove the custom scripts directory (and the enclosed scripts) 29 | # 30 | # rake redmine_git_hosting:remove_scripts RAILS_ENV=xxxx 31 | # 32 | namespace :redmine_git_hosting do 33 | desc "Reload defaults from init.rb into the redmine_git_hosting settings." 34 | task :restore_defaults => [:environment] do 35 | if defined?(Rails) && Rails.logger 36 | Rails.logger.auto_flushing = true if Rails.logger.respond_to?(:auto_flushing=) 37 | Rails.logger.warn "\n\nReinitializing settings from init.rb (via rake at #{my_date})" 38 | end 39 | puts "[Reloading defaults from init.rb:" 40 | default_hash = Redmine::Plugin.find("redmine_git_hosting").settings[:default] 41 | if default_hash.nil? || default_hash.empty? 42 | puts " No defaults specified in init.rb!" 43 | else 44 | changes = 0 45 | valuehash = (Setting.plugin_redmine_git_hosting).clone 46 | default_hash.each do |key,value| 47 | if valuehash[key] != value 48 | print " Changing '#{key}': '#{valuehash[key]}' => '#{value}'\n" 49 | valuehash[key] = value 50 | changes += 1 51 | end 52 | end 53 | if changes == 0 54 | print " No changes necessary.\n" 55 | else 56 | print " Committing changes ... " 57 | begin 58 | Setting.plugin_redmine_git_hosting = valuehash 59 | print "Success!\n" 60 | rescue 61 | print "Failure.\n" 62 | end 63 | end 64 | end 65 | puts "DONE.]" 66 | end 67 | 68 | desc "Update/repair gitolite configuration" 69 | task :update_repositories => [:environment] do 70 | puts "[Performing manual update_repositories operation..." 71 | if defined?(Rails) && Rails.logger 72 | Rails.logger.auto_flushing = true if Rails.logger.respond_to?(:auto_flushing=) 73 | Rails.logger.warn "\n\nPerforming manual UpdateRepositories from command line (via rake at #{my_date})" 74 | end 75 | GitHosting.update_repositories(:resync_all => true) 76 | puts "DONE.]" 77 | end 78 | 79 | desc "Fetch commits from gitolite repositories/update gitolite configuration" 80 | task :fetch_changesets => [:environment] do 81 | puts "[Performing manual fetch_changesets operation..." 82 | if defined?(Rails) && Rails.logger 83 | Rails.logger.auto_flushing = true if Rails.logger.respond_to?(:auto_flushing=) 84 | Rails.logger.warn "\n\nPerforming manual FetchChangesets from command line (via rake at #{my_date})" 85 | end 86 | Repository.fetch_changesets 87 | puts "DONE.]" 88 | end 89 | 90 | desc "Install redmine_git_hosting scripts" 91 | task :install_scripts do |t,args| 92 | if !ENV["READ_ONLY"] 93 | ENV["READ_ONLY"] = "false" 94 | end 95 | Rake::Task["selinux:redmine_git_hosting:install_scripts"].invoke 96 | end 97 | 98 | desc "Remove redmine_git_hosting scripts" 99 | task :remove_scripts do 100 | Rake::Task["selinux:redmine_git_hosting:remove_scripts"].invoke 101 | end 102 | end 103 | 104 | # Produce date string of form used by redmine logs 105 | def my_date 106 | Time.now.strftime("%Y-%m-%d %H:%M:%S") 107 | end 108 | -------------------------------------------------------------------------------- /app/helpers/git_hosting_helper.rb: -------------------------------------------------------------------------------- 1 | require "uri" 2 | require "net/http" 3 | 4 | module GitHostingHelper 5 | 6 | def self.git_daemon_enabled(repository, value) 7 | gd = 1 8 | if repository && !repository.extra.nil? 9 | gd = repository.extra[:git_daemon] ? repository.extra[:git_daemon] : gd 10 | end 11 | gd = repository.project.is_public ? gd : 0 12 | return return_selected_string(gd, value) 13 | end 14 | 15 | def self.git_http_enabled(repository, value) 16 | gh = 1 17 | if repository && !repository.extra.nil? 18 | gh = repository.extra[:git_http] ? repository.extra[:git_http] : gh 19 | end 20 | return return_selected_string(gh, value) 21 | end 22 | 23 | def self.git_notify_cia(repository, value) 24 | nc = 0 25 | if repository && !repository.extra.nil? 26 | nc = repository.extra[:notify_cia] ? repository.extra[:notify_cia] : nc 27 | end 28 | return return_selected_string(nc, value) 29 | end 30 | 31 | def self.return_selected_string(found_value, to_check_value) 32 | return "selected='selected'" if (found_value == to_check_value) 33 | return "" 34 | end 35 | 36 | def self.can_create_mirrors(project) 37 | return User.current.allowed_to?(:create_repository_mirrors, project) 38 | end 39 | def self.can_view_mirrors(project) 40 | return User.current.allowed_to?(:view_repository_mirrors, project) 41 | end 42 | def self.can_edit_mirrors(project) 43 | return User.current.allowed_to?(:edit_repository_mirrors, project) 44 | end 45 | 46 | def self.can_create_post_receive_urls(project) 47 | return User.current.allowed_to?(:create_repository_post_receive_urls, project) 48 | end 49 | def self.can_view_post_receive_urls(project) 50 | return User.current.allowed_to?(:view_repository_post_receive_urls, project) 51 | end 52 | def self.can_edit_post_receive_urls(project) 53 | return User.current.allowed_to?(:edit_repository_post_receive_urls, project) 54 | end 55 | 56 | def self.can_create_deployment_keys(project) 57 | return User.current.admin? || User.current.allowed_to?(:create_deployment_keys, project) 58 | end 59 | def self.can_view_deployment_keys(project) 60 | return User.current.admin? || User.current.allowed_to?(:view_deployment_keys, project) 61 | end 62 | def self.can_edit_deployment_keys(project) 63 | return User.current.admin? || User.current.allowed_to?(:edit_deployment_keys, project) 64 | end 65 | def self.can_create_deployment_keys_for_some_project(theuser=User.current) 66 | return true if theuser.admin? 67 | theuser.projects_by_role.each_key do |role| 68 | return true if role.allowed_to?(:create_deployment_keys) 69 | end 70 | false 71 | end 72 | 73 | @@file_actions = { 74 | "a" => "add", 75 | "m" => "modify", 76 | "r" => "remove", 77 | "d" => "remove" 78 | } 79 | 80 | @http_server = nil 81 | 82 | def url_for_revision(revision) 83 | rev = revision.respond_to?(:identifier) ? revision.identifier : revision 84 | shorten_url( 85 | url_for(:controller => 'repositories', :action => 'revision', :id => revision.project, 86 | :rev => rev, :only_path => false, :host => Setting['host_name'], :protocol => Setting['protocol'] 87 | ) 88 | ) 89 | end 90 | 91 | def url_for_revision_path(revision, path) 92 | rev = revision.respond_to?(:identifier) ? revision.identifier : revision 93 | shorten_url( 94 | url_for(:controller => 'repositories', :action => 'entry', :id => revision.project, 95 | :rev => rev, :path => path, :only_path => false, :host => Setting['host_name'], 96 | :protocol => Setting['protocol'] 97 | ) 98 | ) 99 | end 100 | 101 | def map_file_action(action) 102 | @@file_actions.fetch(action.downcase, action) 103 | end 104 | 105 | def shorten_url(url) 106 | if @http_server.nil? 107 | @uri = URI.parse("http://tinyurl.com/api-create.php") 108 | @http_server = Net::HTTP.new(@uri.host, @uri.port) 109 | @http_server.open_timeout = 1 # in seconds 110 | @http_server.read_timeout = 1 # in seconds 111 | end 112 | uri = @uri 113 | uri.query = "url=#{url}" 114 | request = Net::HTTP::Get.new(uri.request_uri) 115 | begin 116 | response = @http_server.request(request) 117 | GitHosting.logger.debug "Shortened URL is: #{response.body}" 118 | return response.body 119 | rescue Exception => e 120 | GitHosting.logger.warn "Failed to shorten url: #{e}" 121 | return url 122 | end 123 | end 124 | 125 | end 126 | -------------------------------------------------------------------------------- /app/controllers/repository_post_receive_urls_controller.rb: -------------------------------------------------------------------------------- 1 | class RepositoryPostReceiveUrlsController < ApplicationController 2 | unloadable 3 | 4 | before_filter :require_login 5 | before_filter :set_user_variable 6 | before_filter :set_repository_variable 7 | before_filter :set_project_variable 8 | before_filter :check_required_permissions 9 | before_filter :check_xhr_request 10 | before_filter :find_repository_post_receive_url, :except => [:index, :create] 11 | 12 | menu_item :settings, :only => :settings 13 | 14 | layout Proc.new { |controller| controller.request.xhr? ? 'popup' : 'base' } 15 | 16 | def index 17 | render_404 18 | end 19 | 20 | def create 21 | @prurl = RepositoryPostReceiveUrl.new(params[:repository_post_receive_urls]) 22 | if request.get? 23 | # display create view 24 | else 25 | @prurl.update_attributes(params[:repository_post_receive_urls]) 26 | @prurl.repository = @repository 27 | 28 | if @prurl.save 29 | flash[:notice] = l(:post_receive_url_notice_created) 30 | 31 | redirect_url = success_url 32 | respond_to do |format| 33 | format.html { 34 | redirect_to redirect_url 35 | } 36 | format.js { 37 | render :update do |page| 38 | page.redirect_to redirect_url 39 | end 40 | } 41 | end 42 | else 43 | respond_to do |format| 44 | format.html { 45 | flash[:error] = l(:post_receive_url_notice_create_failed) 46 | render :action => "create" 47 | } 48 | format.js { 49 | render :action => "form_error" 50 | } 51 | end 52 | end 53 | end 54 | end 55 | 56 | def edit 57 | end 58 | 59 | def update 60 | if @prurl.update_attributes(params[:repository_post_receive_urls]) 61 | flash[:notice] = l(:post_receive_url_notice_updated) 62 | 63 | redirect_url = success_url 64 | respond_to do |format| 65 | format.html { 66 | redirect_to redirect_url 67 | } 68 | format.js { 69 | render :update do |page| 70 | page.redirect_to redirect_url 71 | end 72 | } 73 | end 74 | else 75 | respond_to do |format| 76 | format.html { 77 | flash[:error] = l(:post_receive_url_notice_update_failed) 78 | render :action => "edit" 79 | } 80 | format.js { 81 | render :action => "form_error" 82 | } 83 | 84 | end 85 | end 86 | end 87 | 88 | def destroy 89 | if request.get? 90 | # display confirmation view 91 | else 92 | if params[:confirm] 93 | @prurl.destroy 94 | 95 | flash[:notice] = l(:post_receive_url_notice_deleted) 96 | end 97 | 98 | redirect_url = success_url 99 | respond_to do |format| 100 | format.html { 101 | redirect_to(redirect_url) 102 | } 103 | end 104 | end 105 | end 106 | 107 | def settings 108 | end 109 | 110 | protected 111 | 112 | # This is a success URL to return to basic listing 113 | def success_url 114 | if GitHosting.multi_repos? 115 | url_for(:controller => 'repositories', 116 | :action => 'edit', 117 | :id => @repository.id) 118 | else 119 | url_for(:controller => 'projects', 120 | :action => 'settings', 121 | :id => @project.id, 122 | :tab => 'repository') 123 | end 124 | end 125 | 126 | def set_user_variable 127 | @user = User.current 128 | end 129 | 130 | def set_repository_variable 131 | @repository = Repository.find_by_id(params[:repository_id]) 132 | if !@repository 133 | render_404 134 | end 135 | end 136 | 137 | def set_project_variable 138 | @project = @repository.project 139 | if !@project 140 | render_404 141 | end 142 | end 143 | 144 | def find_repository_post_receive_url 145 | prurl = RepositoryPostReceiveUrl.find_by_id(params[:id]) 146 | 147 | @prurls = @repository.repository_post_receive_urls 148 | 149 | if prurl and prurl.repository == @repository 150 | @prurl = prurl 151 | elsif prurl 152 | render_403 153 | else 154 | render_404 155 | end 156 | end 157 | 158 | def check_required_permissions 159 | # Deny access if the curreent user is not allowed to manage the project's repositoy 160 | if not @project.module_enabled?(:repository) 161 | render_403 162 | end 163 | not_enough_perms = true 164 | @user.roles_for_project(@project).each{|role| 165 | if role.allowed_to? :manage_repository 166 | not_enough_perms = false 167 | break 168 | end 169 | } 170 | if not_enough_perms 171 | render_403 172 | end 173 | end 174 | 175 | def check_xhr_request 176 | @is_xhr ||= request.xhr? 177 | end 178 | 179 | end 180 | -------------------------------------------------------------------------------- /app/controllers/repository_mirrors_controller.rb: -------------------------------------------------------------------------------- 1 | class RepositoryMirrorsController < ApplicationController 2 | unloadable 3 | 4 | before_filter :require_login 5 | before_filter :set_user_variable 6 | before_filter :set_repository_variable 7 | before_filter :set_project_variable 8 | before_filter :check_required_permissions 9 | before_filter :check_xhr_request 10 | before_filter :find_repository_mirror, :except => [:index, :create] 11 | 12 | menu_item :settings, :only => :settings 13 | 14 | layout Proc.new { |controller| controller.request.xhr? ? 'popup' : 'base' } 15 | 16 | def index 17 | render_404 18 | end 19 | 20 | def create 21 | @mirror = RepositoryMirror.new(params[:repository_mirrors]) 22 | if request.get? 23 | # display create view 24 | else 25 | @mirror.update_attributes(params[:repository_mirrors]) 26 | @mirror.repository = @repository 27 | 28 | if @mirror.save 29 | flash[:notice] = l(:mirror_notice_created) 30 | 31 | redirect_url = success_url 32 | respond_to do |format| 33 | format.html { 34 | redirect_to redirect_url 35 | } 36 | format.js { 37 | render :update do |page| 38 | page.redirect_to redirect_url 39 | end 40 | } 41 | end 42 | else 43 | respond_to do |format| 44 | format.html { 45 | flash[:error] = l(:mirror_notice_create_failed) 46 | render :action => "create" 47 | } 48 | format.js { 49 | render :action => "form_error" 50 | } 51 | end 52 | end 53 | end 54 | end 55 | 56 | def edit 57 | end 58 | 59 | def update 60 | if @mirror.update_attributes(params[:repository_mirrors]) 61 | flash[:notice] = l(:mirror_notice_updated) 62 | 63 | redirect_url = success_url 64 | respond_to do |format| 65 | format.html { 66 | redirect_to redirect_url 67 | } 68 | format.js { 69 | render :update do |page| 70 | page.redirect_to redirect_url 71 | end 72 | } 73 | end 74 | else 75 | respond_to do |format| 76 | format.html { 77 | flash[:error] = l(:mirror_notice_update_failed) 78 | render :action => "edit" 79 | } 80 | format.js { 81 | render :action => "form_error" 82 | } 83 | end 84 | 85 | end 86 | end 87 | 88 | def destroy 89 | if request.get? 90 | # display confirmation view 91 | else 92 | if params[:confirm] 93 | @mirror.destroy 94 | 95 | flash[:notice] = l(:mirror_notice_deleted) 96 | end 97 | 98 | redirect_url = success_url 99 | respond_to do |format| 100 | format.html { 101 | redirect_to(redirect_url) 102 | } 103 | end 104 | end 105 | end 106 | 107 | def settings 108 | end 109 | 110 | def push 111 | respond_to do |format| 112 | format.html { 113 | (@push_failed,@shellout) = @mirror.push 114 | } 115 | end 116 | end 117 | 118 | protected 119 | 120 | # This is a success URL to return to basic listing 121 | def success_url 122 | if GitHosting.multi_repos? 123 | url_for(:controller => 'repositories', 124 | :action => 'edit', 125 | :id => @repository.id) 126 | else 127 | url_for(:controller => 'projects', 128 | :action => 'settings', 129 | :id => @project.id, 130 | :tab => 'repository') 131 | end 132 | end 133 | 134 | def set_user_variable 135 | @user = User.current 136 | end 137 | 138 | def set_repository_variable 139 | @repository = Repository.find_by_id(params[:repository_id]) 140 | if !@repository 141 | render_404 142 | end 143 | end 144 | 145 | def set_project_variable 146 | @project = @repository.project 147 | if !@project 148 | render_404 149 | end 150 | end 151 | 152 | def find_repository_mirror 153 | mirror = RepositoryMirror.find_by_id(params[:id]) 154 | 155 | @mirrors = @repository.repository_mirrors 156 | 157 | if mirror and mirror.repository == @repository 158 | @mirror = mirror 159 | elsif mirror 160 | render_403 161 | else 162 | render_404 163 | end 164 | end 165 | 166 | def check_required_permissions 167 | # Deny access if the curreent user is not allowed to manage the project's repositoy 168 | if not @project.module_enabled?(:repository) 169 | render_403 170 | end 171 | not_enough_perms = true 172 | @user.roles_for_project(@project).each{|role| 173 | if role.allowed_to? :manage_repository 174 | not_enough_perms = false 175 | break 176 | end 177 | } 178 | if not_enough_perms 179 | render_403 180 | end 181 | end 182 | 183 | def check_xhr_request 184 | @is_xhr ||= request.xhr? 185 | end 186 | 187 | end 188 | -------------------------------------------------------------------------------- /app/controllers/gitolite_public_keys_controller.rb: -------------------------------------------------------------------------------- 1 | class GitolitePublicKeysController < ApplicationController 2 | unloadable 3 | 4 | before_filter :require_login 5 | before_filter :set_user_variable 6 | before_filter :set_users_keys, :except => [:index, :new, :reset_rss_key] 7 | before_filter :find_gitolite_public_key, :except => [:index, :new, :reset_rss_key, :create] 8 | 9 | helper :issues 10 | helper :users 11 | helper :custom_fields 12 | helper :gitolite_public_keys 13 | include GitolitePublicKeysHelper 14 | 15 | def edit 16 | redirect_to url_for(:controller=>'my', :action=>'account', :public_key_id => @gitolite_public_key[:id]) 17 | end 18 | 19 | def destroy 20 | GitHostingObserver.set_update_active(false) 21 | if !request.get? 22 | destroy_key 23 | end 24 | redirect_to @redirect_url 25 | GitHostingObserver.set_update_active(true) 26 | end 27 | 28 | def update 29 | GitHostingObserver.set_update_active(false) 30 | if !request.get? 31 | if params[:save_button] 32 | if @gitolite_public_key.update_attributes(params[:public_key]) 33 | flash[:notice] = l(:notice_public_key_updated, :title=>keylabel(@gitolite_public_key)) 34 | 35 | respond_to do |format| 36 | format.html { redirect_to @redirect_url } 37 | format.js { render :update do |page| page.redirect_to @redirect_url end } 38 | end 39 | else 40 | respond_to do |format| 41 | format.html { 42 | flash[:error] = l(:error_public_key_create_failed) 43 | # This doesn't give back validation errors (messy!) 44 | redirect_to @redirect_url 45 | } 46 | format.js { 47 | render :action => "form_error" 48 | } 49 | end 50 | end 51 | else 52 | destroy_key if params[:delete_button] 53 | 54 | respond_to do |format| 55 | format.html { redirect_to @redirect_url } 56 | format.js { render :update do |page| page.redirect_to @redirect_url end } 57 | end 58 | end 59 | end 60 | GitHostingObserver.set_update_active(true) 61 | end 62 | 63 | def create 64 | GitHostingObserver.set_update_active(false) 65 | @gitolite_public_key = GitolitePublicKey.new(params[:public_key].merge(:user => @user)) 66 | if params[:create_button] 67 | if @gitolite_public_key.save 68 | flash[:notice] = l(:notice_public_key_added, :title=>keylabel(@gitolite_public_key)) 69 | 70 | respond_to do |format| 71 | format.html { redirect_to @redirect_url } 72 | format.js { render :update do |page| page.redirect_to @redirect_url end } 73 | end 74 | else 75 | respond_to do |format| 76 | format.html { 77 | flash[:error] = l(:error_public_key_create_failed) 78 | # This doesn't give back validation errors (messy!) 79 | redirect_to @redirect_url 80 | } 81 | format.js { 82 | render :action => "form_error" 83 | } 84 | end 85 | end 86 | else 87 | respond_to do |format| 88 | format.html { redirect_to @redirect_url } 89 | format.js { render :update do |page| page.redirect_to @redirect_url end } 90 | end 91 | end 92 | GitHostingObserver.set_update_active(true) 93 | end 94 | 95 | protected 96 | 97 | def set_user_variable 98 | if params[:user_id] 99 | @user = (params[:user_id]=='current') ? User.current : User.find_by_id(params[:user_id]) 100 | if @user 101 | @redirect_url = url_for(:controller => 'users', :action => 'edit', :id => params[:user_id], :tab => 'keys') 102 | else 103 | render_404 104 | end 105 | else 106 | @user = User.current 107 | @redirect_url = url_for(:controller => 'my', :action => 'account') 108 | end 109 | end 110 | 111 | def set_users_keys 112 | @gitolite_user_keys = @user.gitolite_public_keys.active.user_key.find(:all,:order => 'title ASC, created_at ASC') 113 | @gitolite_deploy_keys = @user.gitolite_public_keys.active.deploy_key.find(:all,:order => 'title ASC, created_at ASC') 114 | @gitolite_public_keys = @gitolite_user_keys + @gitolite_deploy_keys 115 | end 116 | 117 | def find_gitolite_public_key 118 | key = GitolitePublicKey.find_by_id(params[:id]) 119 | if key and (@user == key.user || @user.admin?) 120 | @gitolite_public_key = key 121 | elsif key 122 | render_403 123 | else 124 | render_404 125 | end 126 | end 127 | 128 | def destroy_key 129 | @gitolite_public_key[:active] = 0 130 | 131 | # Since we are ultimately destroying this key, just force save (since old keys may fail new validations) 132 | @gitolite_public_key.save((Rails::VERSION::STRING.split('.')[0].to_i > 2) ? { :validate => false } : false) 133 | 134 | flash[:notice] = l(:notice_public_key_deleted, :title=>keylabel(@gitolite_public_key)) 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /app/views/deployment_credentials/_view_list.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 | <% if GitHostingHelper.can_create_deployment_keys(@repository.project) %> 4 |
5 | <%= link_to("Add Deployment Credential", url_for(:controller => 'deployment_credentials', :action => 'create_with_key', :repository_id => @repository.id), :class => 'icon icon-add add-deployment-credential' ) %> 6 |
7 | <% end %> 8 | 9 |

Deployment Credentials

10 | 11 | <% if @repository.deployment_credentials.active.any? %> 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | <% @repository.deployment_credentials.active.sort_by {|x| [x.user.login, x.gitolite_public_key.title]}.each do |cred| %> 26 | 27 | 28 | 37 | 38 | 39 | 40 | 46 | 47 | <% end %> 48 | 49 |
OwnerPublic Key NamePublic Key FilePermHonored? 
<%= cred.user.login %> 29 | <% if cred.user == User.current %> 30 | <%= link_to(cred.gitolite_public_key.title, url_for(:controller => 'my', :action => 'account', :public_key_id => cred.gitolite_public_key.id)) %> 31 | <% elsif User.current.admin? %> 32 | <%= link_to(cred.gitolite_public_key.title, url_for(:controller => 'users', :action => 'edit', :id => cred.gitolite_public_key.user.id, :public_key_id => cred.gitolite_public_key.id, :tab => 'keys')) %> 33 | <% else %> 34 | <%= cred.gitolite_public_key.title %> 35 | <% end %> 36 | <%= "keydir/#{cred.gitolite_public_key.identifier}.pub" %><%= cred.perm %><%= checked_image cred.honored? %> 41 | <% if GitHostingHelper.can_edit_deployment_keys(@repository.project) && (User.current.admin?|| User.current == cred.user) %> 42 | <%= link_to(l(:button_edit), url_for(:controller => 'deployment_credentials', :action => 'edit', :repository_id => @repository.id, :id => cred.id), :class => 'icon icon-edit edit-deployment-credential') %> 43 | <%= link_to(l(:button_delete), url_for(:controller => 'deployment_credentials', :action => 'destroy', :repository_id => @repository.id, :id => cred.id), :class => 'icon icon-del delete-deployment-credential') %> 44 | <% end %> 45 |
50 |
51 | <% else %> 52 | 53 | 54 |
No Deployment Credentials Defined
55 | <% end %> 56 |
57 | 58 | 112 | -------------------------------------------------------------------------------- /lib/git_hosting/patches/projects_controller_patch.rb: -------------------------------------------------------------------------------- 1 | require_dependency 'principal' 2 | require_dependency 'user' 3 | require_dependency 'git_hosting' 4 | require_dependency 'projects_controller' 5 | 6 | module GitHosting 7 | module Patches 8 | module ProjectsControllerPatch 9 | def git_repo_init 10 | users = @project.member_principals.map(&:user).compact.uniq 11 | if users.length == 0 12 | membership = Member.new( 13 | :principal=>User.current, 14 | :project_id=>@project.id, 15 | :role_ids=>[3] 16 | ) 17 | membership.save 18 | end 19 | if @project.module_enabled?('repository') && Setting.plugin_redmine_git_hosting['allProjectsUseGit'] == "true" 20 | # Create new repository 21 | repo = Repository.factory("Git") 22 | if GitHosting.multi_repos? 23 | @project.repositories << repo 24 | else 25 | @project.repository = repo 26 | end 27 | end 28 | end 29 | 30 | def disable_git_daemon_if_not_public 31 | # Go through all gitolite repos and diable git_daemon if necessary 32 | @project.gl_repos.each do |repo| 33 | if repo.extra.git_daemon == 1 && (not @project.is_public ) 34 | repo.extra.git_daemon = 0; 35 | repo.extra.save 36 | repo.save # Trigger update_repositories 37 | end 38 | end 39 | end 40 | 41 | def create_with_disable_update 42 | # Turn of updates during repository update 43 | GitHostingObserver.set_update_active(false); 44 | 45 | # Do actual creation 46 | create_without_disable_update 47 | 48 | # Only create/fixup repo if project creation worked 49 | if validate_parent_id && @project.save 50 | # Fix up repository 51 | git_repo_init 52 | 53 | # Adjust daemon status 54 | disable_git_daemon_if_not_public 55 | end 56 | 57 | # Reenable updates to perform a single update 58 | GitHostingObserver.set_update_active(true); 59 | end 60 | 61 | def update_with_disable_update 62 | # Turn of updates during repository update 63 | GitHostingObserver.set_update_active(false); 64 | 65 | # Do actual update 66 | update_without_disable_update 67 | 68 | # Adjust daemon status 69 | disable_git_daemon_if_not_public 70 | 71 | if @project.gl_repos.detect {|repo| repo.url != GitHosting::repository_path(repo) || repo.url != repo.root_url} 72 | # Hm... something about parent hierarchy changed. Update us and our children 73 | GitHostingObserver.set_update_active(@project, :descendants) 74 | else 75 | # Reenable updates to perform a single update 76 | GitHostingObserver.set_update_active(true); 77 | end 78 | end 79 | 80 | def destroy_with_disable_update 81 | # Turn of updates during repository update 82 | GitHostingObserver.set_update_active(false); 83 | 84 | # Do actual update 85 | destroy_without_disable_update 86 | 87 | # Reenable updates to perform a single update 88 | GitHostingObserver.set_update_active(true); 89 | end 90 | 91 | def archive_with_disable_update 92 | # Turn of updates during repository update 93 | GitHostingObserver.set_update_active(false); 94 | 95 | # Do actual update 96 | archive_without_disable_update 97 | 98 | # Reenable updates to perform a single update 99 | GitHostingObserver.set_update_active(@project, :archive); 100 | end 101 | 102 | def unarchive_with_disable_update 103 | # Turn of updates during repository update 104 | GitHostingObserver.set_update_active(false); 105 | 106 | # Do actual update 107 | unarchive_without_disable_update 108 | 109 | # Reenable updates to perform a single update 110 | GitHostingObserver.set_update_active(@project); 111 | end 112 | 113 | def settings_with_disable_update 114 | # Turn of updates during repository update 115 | GitHostingObserver.set_update_active(false); 116 | 117 | # Do actual update 118 | settings_without_disable_update 119 | 120 | # Reenable updates to perform a single update 121 | if @project.module_enabled?(:repository) 122 | GitHostingObserver.set_update_active(@project); 123 | else 124 | GitHostingObserver.set_update_active(:archive); 125 | end 126 | end 127 | 128 | def self.included(base) 129 | base.class_eval do 130 | unloadable 131 | end 132 | base.send(:alias_method_chain, :create, :disable_update) 133 | base.send(:alias_method_chain, :update, :disable_update) 134 | base.send(:alias_method_chain, :destroy, :disable_update) 135 | base.send(:alias_method_chain, :archive, :disable_update) 136 | base.send(:alias_method_chain, :unarchive, :disable_update) 137 | base.send(:alias_method_chain, :settings, :disable_update) 138 | end 139 | end 140 | end 141 | end 142 | 143 | # Patch in changes 144 | ProjectsController.send(:include, GitHosting::Patches::ProjectsControllerPatch) 145 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | require 'redmine' 3 | 4 | require File.join(File.dirname(__FILE__), 'app', 'models', 'git_repository_extra') 5 | require File.join(File.dirname(__FILE__), 'app', 'models', 'git_cia_notification') 6 | 7 | Redmine::Plugin.register :redmine_git_hosting do 8 | name 'Redmine Git Hosting Plugin' 9 | author 'Eric Bishop, Pedro Algarvio, Christian Käser, Zsolt Parragi, Yunsang Choi, Joshua Hogendorn, Jan Schulz-Hofen, John Kubiatowicz and others' 10 | description 'Enables Redmine / ChiliProject to control hosting of git repositories' 11 | version '0.5.1x' 12 | url 'https://github.com/ericpaulbishop/redmine_git_hosting' 13 | 14 | settings :default => { 15 | 'httpServer' => 'localhost', 16 | 'httpServerSubdir' => '', 17 | 'gitServer' => 'localhost', 18 | 'gitUser' => 'git', 19 | 'gitConfigPath' => 'gitolite.conf', # Redmine gitolite config file 20 | 'gitConfigHasAdminKey' => 'true', # Conf file should have admin key 21 | 'gitRepositoryBasePath' => 'repositories/', 22 | 'gitRedmineSubdir' => '', 23 | 'gitRepositoryHierarchy' => 'true', 24 | 'gitRecycleBasePath' => 'recycle_bin/', 25 | 'gitRecycleExpireTime' => '24.0', 26 | 'gitLockWaitTime' => '10', 27 | 'gitoliteIdentityFile' => RAILS_ROOT + '/.ssh/gitolite_admin_id_rsa', 28 | 'gitoliteIdentityPublicKeyFile' => RAILS_ROOT + '/.ssh/gitolite_admin_id_rsa.pub', 29 | 'allProjectsUseGit' => 'false', 30 | 'gitDaemonDefault' => '1', # Default is Daemon enabled 31 | 'gitHttpDefault' => '1', # Default is HTTP_ONLY 32 | 'gitNotifyCIADefault' => '0', # Default is CIA Notification disabled 33 | 'deleteGitRepositories' => 'false', 34 | 'gitRepositoriesShowUrl' => 'true', 35 | 'gitCacheMaxTime' => '-1', 36 | 'gitCacheMaxElements' => '100', 37 | 'gitCacheMaxSize' => '16', 38 | 'gitHooksDebug' => 'false', 39 | 'gitHooksAreAsynchronous' => 'true', 40 | 'gitTempDataDir' => '/tmp/redmine_git_hosting/', 41 | 'gitScriptDir' => '', 42 | 'gitForceHooksUpdate' => 'true', 43 | 'gitRepositoryIdentUnique' => 'true' 44 | }, 45 | :partial => 'redmine_git_hosting' 46 | project_module :repository do 47 | permission :create_repository_mirrors, :repository_mirrors => :create 48 | permission :view_repository_mirrors, :repository_mirrors => :index 49 | permission :edit_repository_mirrors, :repository_mirrors => :edit 50 | permission :create_repository_post_receive_urls, :repository_post_receive_urls => :create 51 | permission :view_repository_post_receive_urls, :repository_post_receive_urls => :index 52 | permission :edit_repository_post_receive_urls, :repository_post_receive_urls => :edit 53 | permission :create_deployment_keys, :deployment_credentials => :create_with_key 54 | permission :view_deployment_keys, :deployment_credentials => :index 55 | permission :edit_deployment_keys, :deployment_credentials => :edit 56 | end 57 | end 58 | 59 | # Set up autoload of patches 60 | require 'dispatcher' unless Rails::VERSION::MAJOR >= 3 61 | def git_hosting_patch(&block) 62 | if Rails::VERSION::MAJOR >= 3 63 | ActionDispatch::Calbacks.to_prepare(&block) 64 | else 65 | Dispatcher.to_prepare(:redmine_git_patches,&block) 66 | end 67 | end 68 | git_hosting_patch do 69 | patches=Dir[File.dirname(__FILE__)+"/lib/git_hosting/patches/*.rb"].map{|x| File.basename(x,".rb")}.sort 70 | 71 | # Special positioning necessary 72 | # Put git_adapter_patch last (make sure that git_cmd stays patched!) 73 | patches = (patches-["git_adapter_patch"]) << "git_adapter_patch" 74 | patches.each do |patch| 75 | require_dependency 'git_hosting/patches/'+File.basename(patch,".rb") 76 | end 77 | end 78 | 79 | # initialize hooks 80 | class GitProjectShowHook < Redmine::Hook::ViewListener 81 | render_on :view_projects_show_left, :partial => 'git_urls' 82 | end 83 | 84 | class GitRepoUrlHook < Redmine::Hook::ViewListener 85 | render_on :view_repositories_show_contextual, :partial => 'git_urls' 86 | end 87 | 88 | # Put Git SCM first in list of SCMs 89 | Redmine::Scm::Base.all.unshift("Git").uniq! 90 | 91 | # initialize observer 92 | # Don't initialize this while doing migration of primary system (i.e. Redmine/Chiliproject) 93 | migrating_primary = (File.basename($0) == "rake" && ARGV.include?("db:migrate")) 94 | config.after_initialize do 95 | if config.action_controller.perform_caching && !migrating_primary 96 | ActiveRecord::Base.observers = ActiveRecord::Base.observers << GitHostingObserver 97 | ActiveRecord::Base.observers = ActiveRecord::Base.observers << GitHostingSettingsObserver 98 | 99 | ActionController::Dispatcher.to_prepare(:git_hosting_observer_reload) do 100 | GitHostingObserver.instance.reload_this_observer 101 | end 102 | ActionController::Dispatcher.to_prepare(:git_hosting_settings_observer_reload) do 103 | GitHostingSettingsObserver.instance.reload_this_observer 104 | end 105 | end 106 | end 107 | -------------------------------------------------------------------------------- /assets/stylesheets/git_url_display.css: -------------------------------------------------------------------------------- 1 | 2 | #git_url_box 3 | { 4 | height: 75px; 5 | padding: 10px 0 0; 6 | margin:0px; 7 | } 8 | 9 | #git_url_box ul li, 10 | .box #git_url_box ul li 11 | { 12 | list-style-image:none; 13 | padding:0px; 14 | margin:0px; 15 | } 16 | 17 | #git_url_box ul li:hover, 18 | .box #git_url_box ul li:hover 19 | { 20 | list-style-image:none; 21 | margin:0px; 22 | padding:0px; 23 | margin:0px; 24 | } 25 | 26 | 27 | #git_url_list 28 | { 29 | height: 23px; 30 | float: left; 31 | margin: 0px; 32 | padding: 0px 0px 0px 2px; 33 | list-style-image:none; 34 | } 35 | 36 | #git_url_list li 37 | { 38 | float: left; 39 | list-style-type: none; 40 | margin: 0px; 41 | padding: 0px; 42 | list-style-image:none; 43 | } 44 | 45 | #git_url_list li:hover 46 | { 47 | list-style-image:none; 48 | } 49 | 50 | #git_url_list li:first-child a 51 | { 52 | border-left-width: 1px; 53 | 54 | /* Standard, Opera 10, IE 9 */ 55 | border-top-left-radius: 3px; 56 | border-bottom-left-radius: 3px; 57 | /* Konquerer */ 58 | -khtml-border-top-left-radius: 3px; 59 | -khtml-border-bottom-left-radius: 3px; 60 | /* Gecko (Firefox, ...) */ 61 | -moz-border-radius: 3px 0 0 3px; 62 | /* Webkit (Chrome, Safari, ...) */ 63 | -webkit-border-top-left-radius: 3px; 64 | -webkit-border-bottom-left-radius: 3px; 65 | /* IE <= 9 not supported */ 66 | } 67 | 68 | #git_url_list li a 69 | { 70 | background-color: #eee; 71 | background: url(../images/button.svg) 0 0 repeat; /* Opera needs an "image" :( - using svg for this so it will scale properly without looking too ugly */ 72 | background: -khtml-gradient(linear, left top, left bottom, from(#f8f8f8), to(#ddd)); /* Konquerer */ 73 | background: -moz-linear-gradient(top, #f8f8f8, #ddd); /* Gecko (Firefox, ...) */ 74 | background: -webkit-gradient(linear, left top, left bottom, from(#f8f8f8), to(#ddd)); /* Webkit (Chrome, Safari, ...) */ 75 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f8f8f8', endColorstr='#dddddd'); /* IE 5.5 - 7 */ 76 | -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f8f8f8', endColorstr='#dddddd'); /* IE 8 */ 77 | 78 | border-color: #bbb; 79 | border-style: solid; 80 | border-width: 1px 1px 1px 0; 81 | 82 | color: #333; 83 | display: block; 84 | font-size: 11px; 85 | font-weight: bold; 86 | line-height: 21px; 87 | margin: 0; 88 | padding: 0 10px 0 11px; 89 | text-decoration: none; 90 | text-shadow: 1px 1px 0 #fff; 91 | outline: none; 92 | } 93 | 94 | #git_url_list li a:hover, 95 | #git_url_list li a:focus 96 | { 97 | background-color: #507AAA; 98 | background: url(../images/button_focus.svg) 0 0 repeat; /* Opera needs an "image" :( - using svg for this so it will scale properly without looking too ugly */ 99 | background: -khtml-gradient(linear, left top, left bottom, from(#759fcf), to(#507AAA)); /* Konquerer */ 100 | background: -moz-linear-gradient(top, #759fcf, #507AAA); /* Gecko (Firefox, ...) */ 101 | background: -webkit-gradient(linear, left top, left bottom, from(#759fcf), to(#507AAA)); /* Webkit (Chrome, Safari, ...) */ 102 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#759fcf', endColorstr='#507AAA'); /* IE 5.5 - IE 7 */ 103 | -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#759fcf', endColorstr='#507AAA'); /* IE 8 */ 104 | 105 | color: #fff; 106 | text-shadow: -1px -1px 0 rgba(0,0,0,0.4); 107 | border-top-color: #759fcf; 108 | border-bottom-color: #507AAA; 109 | } 110 | 111 | #git_url_list li a.selected 112 | { 113 | background-color: #bbb; 114 | background: url(../images/button_selected.svg) 0 0 repeat; /* Opera needs an "image" :( - using svg for this so it will scale properly without looking too ugly */ 115 | background: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#aaa)); /* Konquerer */ 116 | background: -moz-linear-gradient(top, #ccc, #aaa); /* Gecko (Firefox, ...) */ 117 | background: -webkit-gradient(linear, left top, left bottom, from(#ccc), to(#aaa)); /* Webkit (Chrome, Safari, ...) */ 118 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#aaaaaa'); /* IE 5.5 - IE 7 */ 119 | -ms-filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#cccccc', endColorstr='#aaaaaa'); /* IE 8 */ 120 | 121 | color: #000; 122 | text-shadow: 1px 1px 0 rgba(255,255,255,0.4); 123 | border-color: #bbb; 124 | } 125 | 126 | #git_url_text 127 | { 128 | border: 1px solid #bbb; 129 | border-width: 1px 1px 1px 1px; 130 | background-color: #fff; 131 | color: #000; 132 | font-size: 11px; 133 | height: 16px; 134 | padding: 3px 5px 2px; 135 | min-width:275px; 136 | width: 75%; 137 | font-family: Monaco,"DejaVu Sans Mono","Bitstream Vera Sans Mono","Courier New",monospace; 138 | margin: 0; 139 | float: left; 140 | } 141 | 142 | #git_url_desc 143 | { 144 | color: #666; 145 | line-height: 23px; 146 | font-size: 11px; 147 | float: left; 148 | margin: 0 0 0 5px; 149 | } 150 | 151 | #git_url_access 152 | { 153 | font-weight: bold; 154 | } 155 | 156 | -------------------------------------------------------------------------------- /db/migrate/20120904060609_update_multi_repo_per_project.rb: -------------------------------------------------------------------------------- 1 | class UpdateMultiRepoPerProject < ActiveRecord::Migration 2 | def self.up 3 | if !columns("repository_mirrors").index{|x| x.name=="repository_id"} 4 | add_column :repository_mirrors, :repository_id, :integer 5 | begin 6 | say "Detaching repository mirrors from projects; attaching them to repositories..." 7 | RepositoryMirror.all.each do |mirror| 8 | mirror.repository_id = Project.find(mirror.project_id).repository.id 9 | mirror.save! 10 | end 11 | say "Success. Changed #{RepositoryMirror.all.count} records." 12 | rescue => e 13 | say "Failed to attach repository mirrors to repositories." 14 | say "Error: #{e.message}" 15 | end 16 | if columns("repository_mirrors").index{|x| x.name=="project_id"} 17 | remove_column :repository_mirrors, :project_id 18 | end 19 | end 20 | 21 | if !columns("repository_post_receive_urls").index{|x| x.name=="repository_id"} 22 | add_column :repository_post_receive_urls, :repository_id, :integer 23 | begin 24 | say "Detaching repository post-receive-urls from projects; attaching them to repositories..." 25 | RepositoryPostReceiveUrl.all.each do |prurl| 26 | prurl.repository_id = Project.find(prurl.project_id).repository.id 27 | prurl.save! 28 | end 29 | say "Success. Changed #{RepositoryPostReceiveUrl.all.count} records." 30 | rescue => e 31 | say "Failed to attach repositories post-receive-urls to repositories." 32 | say "Error: #{e.message}" 33 | end 34 | if columns("repository_post_receive_urls").index{|x| x.name=="project_id"} 35 | remove_column :repository_post_receive_urls, :project_id 36 | end 37 | end 38 | 39 | add_index :projects, [:identifier] 40 | if columns("repositories").index{|x| x.name=="identifier"} 41 | add_index :repositories, [:identifier] 42 | add_index :repositories, [:identifier, :project_id] 43 | end 44 | rename_column :git_caches, :proj_identifier, :repo_identifier 45 | 46 | begin 47 | # Add some new settings to settings page, if they don't exist 48 | valuehash = (Setting.plugin_redmine_git_hosting).clone 49 | if ((Repository.all.map(&:identifier).inject(Hash.new(0)) do |h,x| 50 | h[x]+=1 unless x.blank? 51 | h 52 | end.values.max) || 0) > 1 53 | # Oops -- have duplication. Force to false. 54 | valuehash['gitRepositoryIdentUnique'] = "false" 55 | else 56 | # If no duplication -- set to true only if it doesn't already exist 57 | valuehash['gitRepositoryIdentUnique'] ||= 'true' 58 | end 59 | 60 | if (Setting.plugin_redmine_git_hosting != valuehash) 61 | say "Added redmine_git_hosting settings: 'gitRepositoryIdentUnique' => #{valuehash['gitRepositoryIdentUnique']}" 62 | Setting.plugin_redmine_git_hosting = valuehash 63 | end 64 | rescue => e 65 | # ignore problems if plugin settings don't exist yet 66 | end 67 | end 68 | 69 | def self.down 70 | if !columns("repository_mirrors").index{|x| x.name=="project_id"} 71 | add_column :repository_mirrors, :project_id, :integer 72 | begin 73 | say "Detaching repository mirrors from repositories; re-attaching them to projects..." 74 | RepositoryMirror.all.each do |mirror| 75 | mirror.project_id = Repository.find(mirror.repository_id).project.id 76 | mirror.save! 77 | end 78 | say "Success. Changed #{RepositoryMirror.all.count} records." 79 | rescue => e 80 | say "Failed to re-attach repository mirrors to projects." 81 | say "Error: #{e.message}" 82 | end 83 | if columns("repository_mirrors").index{|x| x.name=="repository_id"} 84 | remove_column :repository_mirrors, :repository_id 85 | end 86 | end 87 | 88 | if !columns("repository_post_receive_urls").index{|x| x.name=="project_id"} 89 | add_column :repository_post_receive_urls, :project_id, :integer 90 | begin 91 | say "Detaching repository post-receive-urls from repositories; re-attaching them to projects..." 92 | RepositoryPostReceiveUrl.all.each do |prurl| 93 | prurl.project_id = Repository.find(prurl.repository_id).project.id 94 | prurl.save! 95 | end 96 | say "Success. Changed #{RepositoryPostReceiveUrl.all.count} records." 97 | rescue => e 98 | say "Failed to re-attach repository post-receive urls to projects." 99 | say "Error: #{e.message}" 100 | end 101 | if columns("repository_post_receive_urls").index{|x| x.name=="repository_id"} 102 | remove_column :repository_post_receive_urls, :repository_id 103 | end 104 | end 105 | 106 | remove_index :projects, [:identifier] 107 | if columns("repositories").index{|x| x.name=="identifier"} 108 | remove_index :repositories, [:identifier] 109 | remove_index :repositories, [:identifier, :project_id] 110 | end 111 | rename_column :git_caches, :repo_identifier, :proj_identifier 112 | 113 | begin 114 | # Remove above settings from plugin page 115 | valuehash = (Setting.plugin_redmine_git_hosting).clone 116 | valuehash.delete('gitRepositoryIdentUnique') 117 | 118 | if (Setting.plugin_redmine_git_hosting != valuehash) 119 | say "Removed redmine_git_hosting settings: 'gitRepositoryIdentUnique'" 120 | Setting.plugin_redmine_git_hosting = valuehash 121 | end 122 | rescue => e 123 | # ignore problems if table doesn't exist yet.... 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /lib/gitolite_recycle.rb: -------------------------------------------------------------------------------- 1 | module GitHosting 2 | # This class implements a basic recycle bit for repositories deleted from the gitolite repository 3 | # 4 | # Whenever repositories are deleted, we rename them and place them in the recycle_bin. 5 | # Assuming that GitoliteRecycle.delete_expired_files is called regularly, files in the recycle_bin 6 | # older than 'preserve_time' will be deleted. Both the path for the recycle_bin and the preserve_time 7 | # are settable as settings. 8 | # 9 | # John Kubiatowicz, 11/21/11 10 | class GitoliteRecycle 11 | TRASH_DIR_SEP = "__" # Separator character(s) used to replace '/' in name 12 | 13 | RECYCLE_BIN_IF_UNDEF = "recycle_bin/" # In case settings not migrated (normally from settings) 14 | PRESERVE_TIME_IF_UNDEF = 1440 # In case settings not migrated (normally from settings) 15 | 16 | def self.logger 17 | return GitHosting.logger 18 | end 19 | 20 | # Recycle bin base path (relative to git user home directory) 21 | def self.recycle_bin 22 | Setting.plugin_redmine_git_hosting['gitRecycleBasePath'] || RECYCLE_BIN_IF_UNDEF 23 | end 24 | 25 | # Recycle preservation time (in minutes) 26 | def self.preserve_time 27 | (Setting.plugin_redmine_git_hosting['gitRecycleExpireTime'].to_f * 60).to_i || PRESERVE_TIME_IF_UNDEF 28 | end 29 | 30 | # Scan through the recyclebin and delete files older than 'preserve_time' minutes 31 | def self.delete_expired_files 32 | return unless GitHosting.file_exists?(recycle_bin) 33 | 34 | result = %x[#{GitHosting.git_user_runner} find '#{recycle_bin}' -type d -regex '.*\.git' -cmin +#{preserve_time} -prune -print].chomp.split("\n") 35 | if result.length > 0 36 | logger.warn "Garbage-collecting expired file#{(result.length != 1) ? "s" : ""} from recycle bin:" 37 | result.each do |filename| 38 | begin 39 | GitHosting.shell %[#{GitHosting.git_user_runner} rm -r #{filename}] 40 | logger.warn " Deleting #{filename}" 41 | rescue 42 | logger.error "GitoliteRecycle.delete_expired_files() failed trying to delete repository #{filename}!" 43 | end 44 | end 45 | 46 | # Optionally remove recycle_bin (but only if empty). Ignore error if non-empty 47 | %x[#{GitHosting.git_user_runner} rmdir #{recycle_bin}] 48 | end 49 | end 50 | 51 | def self.move_repository_to_recycle repo_name 52 | # Only bother if actually exists! 53 | return unless GitHosting.git_repository_exists?(repo_name) 54 | 55 | repo_path = GitHosting.repository_path(repo_name) 56 | new_path = File.join(recycle_bin,"#{Time.now.to_i.to_s}#{TRASH_DIR_SEP}#{name_to_recycle_name(repo_name)}.git") 57 | begin 58 | GitHosting.shell %[#{GitHosting.git_user_runner} mkdir -p '#{recycle_bin}'] 59 | GitHosting.shell %[#{GitHosting.git_user_runner} chmod 770 '#{recycle_bin}'] 60 | GitHosting.shell %[#{GitHosting.git_user_runner} mv '#{repo_path}' '#{new_path}'] 61 | logger.warn " Moving '#{repo_name}' from gitolite repository => '#{new_path}'. Will remain for at least #{preserve_time/60.0} hours" 62 | # If any empty directories left behind, try to delete them. Ignore failure. 63 | old_prefix = repo_name[/.*?(?=\/)/] # Top-level old directory without trailing '/' 64 | if old_prefix 65 | repo_subpath = File.join(GitHosting.repository_base, old_prefix) 66 | result = %x[#{GitHosting.git_user_runner} find '#{repo_subpath}' -depth -type d ! -regex '.*\.git/.*' -empty -delete -print].chomp.split("\n") 67 | result.each { |dir| logger.warn " Removing empty repository subdirectory: #{dir}"} 68 | end 69 | return true 70 | rescue 71 | logger.error "Attempt to move repository '#{repo_name}.git' to recycle bin failed" 72 | return false 73 | end 74 | end 75 | 76 | def self.recover_repository_if_present repo_name 77 | # Pull up any matching repositories. Sort them (beginning is representation of time) 78 | myregex = File.join(recycle_bin,"[0-9]+#{TRASH_DIR_SEP}#{name_to_recycle_name(repo_name)}.git") 79 | files = %x[#{GitHosting.git_user_runner} find '#{recycle_bin}' -type d -regex '#{myregex}' -prune].chomp.split("\n").sort {|x,y| y <=> x } 80 | if files.length > 0 81 | # Found something! 82 | logger.warn " Restoring '#{repo_name}.git' to gitolite repository from recycle bin (#{files.first})" 83 | begin 84 | prefix = repo_name[/.*(?=\/)/] # Complete directory path (if exists) without trailing '/' 85 | if prefix 86 | repo_prefix = File.join(GitHosting.repository_base, prefix) 87 | # Has subdirectory. Must reconstruct directory 88 | GitHosting.shell %[#{GitHosting.git_user_runner} mkdir -p '#{repo_prefix}'] 89 | end 90 | repo_path = GitHosting.repository_path(repo_name) 91 | GitHosting.shell %[#{GitHosting.git_user_runner} mv '#{files.first}' '#{repo_path}'] 92 | 93 | # Optionally remove recycle_bin (but only if empty). Ignore error if non-empty 94 | %x[#{GitHosting.git_user_runner} rmdir #{recycle_bin}] 95 | return true 96 | rescue 97 | logger.error "Attempt to recover '#{repo_name}.git' failed" 98 | return false 99 | end 100 | else 101 | false 102 | end 103 | end 104 | 105 | # This routine takes a name and turns it into a name for the recycle bit, 106 | # where we have a 1-level directory full of deleted repositories which 107 | # we keep for 'preserve_time'. 108 | def self.name_to_recycle_name repo_name 109 | new_trash_name = "#{repo_name}".gsub(/\//,"#{TRASH_DIR_SEP}") 110 | end 111 | 112 | 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /app/models/repository_mirror.rb: -------------------------------------------------------------------------------- 1 | class RepositoryMirror < ActiveRecord::Base 2 | STATUS_ACTIVE = 1 3 | STATUS_INACTIVE = 0 4 | 5 | PUSHMODE_MIRROR = 0 6 | PUSHMODE_FORCE = 1 7 | PUSHMODE_FAST_FORWARD = 2 8 | 9 | belongs_to :repository 10 | 11 | attr_accessible :url, :push_mode, :include_all_branches, :include_all_tags, :explicit_refspec, :active 12 | 13 | validates_uniqueness_of :url, :scope => [:repository_id] 14 | validates_presence_of :repository_id, :url 15 | validates_associated :repository 16 | 17 | validate :check_refspec 18 | 19 | before_validation :strip_whitespace 20 | 21 | named_scope :active, {:conditions => {:active => RepositoryMirror::STATUS_ACTIVE}} 22 | named_scope :inactive, {:conditions => {:active => RepositoryMirror::STATUS_INACTIVE}} 23 | 24 | named_scope :has_explicit_refspec, {:conditions => ['push_mode > 0']} 25 | 26 | def push 27 | repo_path = GitHosting.repository_path(repository) 28 | 29 | push_args = "" 30 | if push_mode == PUSHMODE_MIRROR 31 | push_args << "--mirror " 32 | else 33 | # Not mirroring -- other possible push_args 34 | push_args << "--force " if push_mode == PUSHMODE_FORCE 35 | push_args << "--all " if include_all_branches 36 | push_args << "--tags " if include_all_tags 37 | end 38 | push_args << "\"#{dequote(url)}\" " 39 | push_args << "\"#{dequote(explicit_refspec)}\" " unless explicit_refspec.blank? 40 | 41 | # mycom = %[ echo 'cd "#{repo_path}" ; env GIT_SSH=~/.ssh/run_gitolite_admin_ssh git push #{push_args}2>&1' | #{GitHosting.git_user_runner} "bash" ] 42 | # GitHosting.logger.error "Pushing: #{mycom}" 43 | shellout = %x[ echo 'cd "#{repo_path}" ; env GIT_SSH=~/.ssh/run_gitolite_admin_ssh git push #{push_args}2>&1' | #{GitHosting.git_user_runner} "bash" ].chomp 44 | push_failed = ($?.to_i!=0) ? true : false 45 | if (push_failed) 46 | GitHosting.logger.error "[ Pushing changes to mirror: #{url} ... Failed!" 47 | GitHosting.logger.error " "+shellout.split("\n").join("\n ")+" ]" 48 | else 49 | GitHosting.logger.info "[ Pushing changes to mirror: #{url} ... Succeeded! ]" 50 | end 51 | [push_failed,shellout] 52 | end 53 | 54 | # If we have an explicit refspec, check it against incoming payloads 55 | # Special case: if we do not pass in any payloads, return true 56 | def needs_push(payloads=[]) 57 | return true if payloads.empty? 58 | return true if push_mode==PUSHMODE_MIRROR 59 | 60 | refspec_parse = explicit_refspec.match(/^\+?([^:]*)(:[^:]*)?$/) 61 | payloads.each do |payload| 62 | if splitpath = refcomp_parse(payload[:ref]) 63 | return true if payload[:ref] == refspec_parse[1] # Explicit Reference Spec complete path 64 | return true if splitpath[:name] == refspec_parse[1] # Explicit Reference Spec no type 65 | return true if include_all_branches && splitpath[:type] == "heads" 66 | return true if include_all_tags && splitpath[:type] == "tags" 67 | end 68 | end 69 | false 70 | end 71 | 72 | def to_s 73 | return File.join("#{repository.project.identifier}-#{url}") 74 | end 75 | 76 | protected 77 | 78 | # Strip leading and trailing whitespace 79 | def strip_whitespace 80 | self.url = url.strip 81 | self.explicit_refspec = explicit_refspec.strip 82 | end 83 | 84 | # Put backquote in front of crucial characters 85 | def dequote(in_string) 86 | in_string.gsub(/[$,"\\\n]/) {|x| "\\"+x} 87 | end 88 | 89 | def check_refspec 90 | self.explicit_refspec = explicit_refspec.strip 91 | 92 | if push_mode == PUSHMODE_MIRROR 93 | # clear out all extra parameters.. (we use javascript to hide them anyway) 94 | self.include_all_branches = false 95 | self.include_all_tags = false 96 | self.explicit_refspec = "" 97 | elsif include_all_branches && include_all_tags 98 | errors.add_to_base("Cannot #{l(:field_include_all_branches)} and #{l(:field_include_all_tags)} at the same time.") 99 | errors.add(:explicit_refspec, "cannot be used with #{l(:field_include_all_branches)} or #{l(:field_include_all_tags)}") unless explicit_refspec.blank? 100 | elsif !explicit_refspec.blank? 101 | errors.add(:explicit_refspec, "cannot be used with #{l(:field_include_all_branches)}.") if include_all_branches 102 | 103 | # Check format of refspec 104 | if !(refspec_parse = explicit_refspec.match(/^\+?([^:]*)(:([^:]*))?$/)) || !refcomp_valid(refspec_parse[1]) || !refcomp_valid(refspec_parse[3]) 105 | errors.add(:explicit_refspec, "is badly formatted.") 106 | elsif !refspec_parse[1] || refspec_parse[1]=="" 107 | errors.add(:explicit_refspec, "cannot have null first component (will delete remote branch(s))") 108 | end 109 | elsif !include_all_branches && !include_all_tags 110 | errors.add_to_base("Must include at least one item to push.") 111 | end 112 | end 113 | 114 | def refcomp_valid(spec) 115 | # Allow null or empty components 116 | if !spec || spec=="" || refcomp_parse(spec) 117 | true 118 | else 119 | false 120 | end 121 | end 122 | 123 | # Parse a reference component. Three possibilities: 124 | # 125 | # 1) refs/type/name 126 | # 2) name 127 | # 128 | # here, name can have many components. 129 | @@refcomp = "[\\.\\-\\w_\\*]+" 130 | def refcomp_parse(spec) 131 | if (refcomp_parse = spec.match(/^(refs\/)?((#{@@refcomp})\/)?(#{@@refcomp}(\/#{@@refcomp})*)$/)) 132 | if refcomp_parse[1] 133 | # Should be first class. If no type component, return fail 134 | if refcomp_parse[3] 135 | {:type=>refcomp_parse[3], :name=>refcomp_parse[4]} 136 | else 137 | nil 138 | end 139 | elsif refcomp_parse[3] 140 | {:type=>nil, :name=>(refcomp_parse[3]+"/"+refcomp_parse[4])} 141 | else 142 | {:type=>nil, :name=>refcomp_parse[4]} 143 | end 144 | else 145 | nil 146 | end 147 | end 148 | 149 | end 150 | -------------------------------------------------------------------------------- /app/views/gitolite_public_keys/_view.html.erb: -------------------------------------------------------------------------------- 1 | <%= stylesheet_link_tag('application', :plugin => 'redmine_git_hosting')%> 2 |
3 | <%=l(:label_public_keys)%> 4 | 5 | <% if !@gitolite_user_keys.empty? || @gitolite_deploy_keys.empty? %> 6 |
<%= @gitolite_deploy_keys.empty? ? l(:label_current_public_keys) : l(:label_current_user_keys) %> 7 | 8 | <% if @gitolite_user_keys.empty? %> 9 | 10 | <%end %> 11 | <% @gitolite_user_keys.each do |key| %> 12 | 13 | 14 | <% if params[:id] %> 15 | 16 | <% end %> 17 | 21 | 22 | <% end %> 23 |
<%= h(key) %><%= "keydir/#{key.identifier}.pub" %> 18 | <%= link_to(l(:button_edit), { :public_key_id => key.id, :tab => params[:id]&&'keys'}, :class => 'icon icon-edit') %> 19 | <%= link_to(l(:button_delete), public_key_path(key, :user_id=>params[:id]), :method => 'delete', :class => 'icon icon-del', :confirm=>l(:text_gitolite_key_destroy_confirmation, :title=>keylabel(key))) %> 20 |
24 |

25 | <% end %> 26 | 27 | <% if !@gitolite_deploy_keys.empty? %> 28 |
<%= l(:label_current_deploy_keys)%> 29 | 30 | <% if @gitolite_deploy_keys.empty? %> 31 | 32 | <%end %> 33 | <% @gitolite_deploy_keys.each do |key| %> 34 | 35 | 36 | <% if params[:id] %> 37 | 38 | <% end %> 39 | 43 | 44 | <% end %> 45 |
<%= h(key) %><%= "keydir/#{key.identifier}.pub" %> 40 | <%= link_to(l(:button_edit), { :public_key_id => key.id, :tab => params[:id]&&'keys'}, :class => 'icon icon-edit') %> 41 | <%= link_to(l(:button_delete), public_key_path(key, :user_id=>params[:id]), :method => 'delete', :class => 'icon icon-del', :confirm=>l(:text_gitolite_key_destroy_confirmation, :title=>keylabel(key))) %> 42 |
46 |

47 | <% end %> 48 | 49 | <% @new_key = @gitolite_public_key.new_record? %> 50 |
<%= @new_key ? l(:label_public_key_new) : l(:label_public_key_edit) %> 51 |
<%= error_messages_for 'gitolite_public_key' %>
52 | <% labelled_remote_form_for :public_key, @gitolite_public_key, 53 | :url=>{:controller=>'gitolite_public_keys', :action=>@new_key ? 'create' : 'update', :id=>@gitolite_public_key.id, :user_id=>params[:id], :tab => params[:id]&&'keys'}, 54 | :html => {:method=>(@new_key ? :post : :put)} do |f| %> 55 |

<%= f.text_field :title, :label => :label_identifier_can_be_arbitrary, :required => true, :style => 'width:99%;' %>

56 | <% if @gitolite_public_key.key_type==1 || GitHostingHelper.can_create_deployment_keys_for_some_project(@user) %> 57 |
58 |

59 | <%= f.select :key_type, options_for_select([[l(:label_user_key),0],[l(:label_deploy_key),1]], :selected => @gitolite_public_key.key_type, :disabled => (@new_key ? [] : [0,1])), :required => true, :label => :field_key_type %> 60 |

61 | 66 |
67 | <% end %> 68 |

<%= f.text_area :key, :label => (@new_key? :label_cut_and_paste : :field_public_key), :required => true, :disabled => !@new_key, 69 | :style => "width:99%;height:200px;overflow:auto;", 70 | :cols => nil, :rows => nil %> 71 | <%= hidden_field_tag :button_submit_field, "true" %> 72 | <% if !@new_key%> 73 | <%= l(:label_key_cannot_be_changed_please_create_new_key) %>

74 | <%= submit_tag l(:button_save), :name=>'save_button' %> 75 | <%= submit_tag l(:button_delete), :name=>'delete_button', :confirm => l(:text_gitolite_key_destroy_confirmation,:title=>(@gitolite_public_key[:title].blank? ? l(:text_this_key) : keylabel(@gitolite_public_key))) %> 76 | <%= submit_tag l(:button_cancel), :name=>'cancel_button' %> 77 | <% else %> 78 |

79 | <%= submit_tag l(:button_create), :name=>'create_button' %> 80 | <% mystyle = (@gitolite_public_key.errors.any? ? '' : 'style="display:none;"') %> 81 | > 82 | <%= submit_tag l(:button_cancel), :name=>'cancel_button' %> 83 | 84 | <% end %> 85 | <% end %> 86 |
87 |
88 | 89 | 123 | -------------------------------------------------------------------------------- /app/views/repository_mirrors/_view_list.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 | <% if GitHostingHelper.can_create_mirrors(@repository.project) %> 4 |
5 | <%= link_to("Add Repository Mirror", url_for(:controller => 'repository_mirrors', :action => 'create', :repository_id => @repository.id), :class => 'icon icon-add add-mirror' ) %> 6 |
7 | <% end %> 8 | 9 |

Repository Mirrors

10 | 11 | <% if @repository.repository_mirrors.any? %> 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <% @repository.repository_mirrors.each do |mirror| %> 24 | 25 | 27 | 28 | 29 | 39 | 40 | <% end %> 41 | 42 |
URLRefspecMode 
<%= link_to(h(mirror.url), url_for(:controller => 'repository_mirrors', :action => 'edit', :repository_id => @repository.id, :id => mirror.id), :class => 'edit-mirror') %> 26 | <%= refspec mirror, 64 %><%= mirror_mode mirror %>
<%= checked_image mirror.active? %>
30 | <% if GitHostingHelper.can_edit_mirrors(@repository.project) %> 31 | <% if mirror.active==1 -%> 32 | <%= link_to(l(:button_push), url_for(:controller => 'repository_mirrors', :action => 'push', :repository_id => @repository.id, :id => mirror.id), :title => l(:button_push_title), :alt => l(:button_push_title), :class => 'icon icon-push push-mirror') %> 33 | <% end %> 34 | 35 | <%= link_to(l(:button_edit), url_for(:controller => 'repository_mirrors', :action => 'edit', :repository_id => @repository.id, :id => mirror.id), :class => 'icon icon-edit edit-mirror') %> 36 | <%= link_to(l(:button_delete), url_for(:controller => 'repository_mirrors', :action => 'destroy', :repository_id => @repository.id, :id => mirror.id), :class => 'icon icon-del delete-mirror') %> 37 | <% end %> 38 |
43 |
44 | <% else %> 45 | 46 | 47 |
No Mirrors Defined
48 | <% end %> 49 | 50 | <% mirror_pubkey = GitHosting.mirror_push_public_key %> 51 |
52 |
53 |
<%= image_tag 'paste.png', :plugin => 'redmine_git_hosting' %>
54 |
55 | Mirrors Must Grant Write Access To The Following Public Key: 56 |
57 | 58 |
59 | 60 |
61 |
62 | 63 | <% content_for :header_tags do %> 64 | <%= stylesheet_link_tag('zero_clipboard', :plugin => 'redmine_git_hosting') %> 65 | <%= javascript_include_tag('ZeroClipboard', :plugin => 'redmine_git_hosting') %> 66 | <%= javascript_include_tag('zero_clipboard_setup', :plugin => 'redmine_git_hosting') %> 67 | <% end %> 68 | 69 | 159 | --------------------------------------------------------------------------------