From 98cbb8514b0b3394ecb9fa9894fc93c2ceeda3af Mon Sep 17 00:00:00 2001
From: John Kubiatowicz
Date: Wed, 23 Nov 2011 16:44:25 -0800
Subject: [PATCH 01/11] Added hooks, policy and rakefile to install selinux
policy for this plugin
---
.gitignore | 2 +
README.mkd | 38 +++
app/models/git_hosting_settings_observer.rb | 23 +-
.../settings/_redmine_git_hosting.html.erb | 7 +-
config/locales/bg.yml | 1 +
config/locales/bs.yml | 1 +
config/locales/ca.yml | 1 +
config/locales/cs.yml | 1 +
config/locales/da.yml | 1 +
config/locales/de.yml | 1 +
config/locales/el.yml | 1 +
config/locales/en.yml | 1 +
config/locales/es.yml | 1 +
config/locales/fi.yml | 1 +
config/locales/fr.yml | 1 +
config/locales/gl.yml | 1 +
config/locales/he.yml | 1 +
config/locales/hu.yml | 1 +
config/locales/id.yml | 1 +
config/locales/it.yml | 1 +
config/locales/ja.yml | 1 +
config/locales/ko.yml | 1 +
config/locales/lt.yml | 1 +
config/locales/nl.yml | 1 +
config/locales/no.yml | 1 +
config/locales/pl.yml | 1 +
config/locales/pt-BR.yml | 1 +
config/locales/pt.yml | 1 +
config/locales/ro.yml | 1 +
config/locales/ru.yml | 1 +
config/locales/sk.yml | 1 +
config/locales/sl.yml | 1 +
config/locales/sr.yml | 1 +
config/locales/sv.yml | 1 +
config/locales/th.yml | 1 +
config/locales/tr.yml | 1 +
config/locales/uk.yml | 1 +
config/locales/vi.yml | 1 +
config/locales/zh-TW.yml | 1 +
config/locales/zh.yml | 1 +
lib/git_hosting.rb | 62 +++-
selinux/README | 67 +++++
selinux/redmine_git.fc | 9 +
selinux/redmine_git.if | 131 +++++++++
selinux/redmine_git.pp | Bin 0 -> 127822 bytes
selinux/redmine_git.sh | 44 +++
selinux/redmine_git.te | 69 +++++
tasks/selinux.rake | 273 ++++++++++++++++++
48 files changed, 739 insertions(+), 22 deletions(-)
create mode 100644 selinux/README
create mode 100644 selinux/redmine_git.fc
create mode 100644 selinux/redmine_git.if
create mode 100644 selinux/redmine_git.pp
create mode 100755 selinux/redmine_git.sh
create mode 100644 selinux/redmine_git.te
create mode 100644 tasks/selinux.rake
diff --git a/.gitignore b/.gitignore
index a01ee289f..5412c5a54 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
.*.swp
+bin/
+selinux/tmp/
diff --git a/README.mkd b/README.mkd
index 945b9cf82..dfae03514 100755
--- a/README.mkd
+++ b/README.mkd
@@ -246,6 +246,44 @@ This library allows you to quickly deploy ChiliProject, with this plugin to an u
chili\_test.sh script, modifying the variables in those scripts as desired. This library is still under development,
so these instructions may need to be updated in the near future.
+## Selinux Configuration for redmine
+
+This plugin can be configured to run with selinux. We have included a rakefile in tasks/selinux.rake to assist
+with installing with selinux. You should start by editing init.rb and migrating as described above. Then, you
+can execute one of the selinux rake tasks (from the redmine root). For instance, the simplest option installs
+a selinux configuration for both redmine and the redmine_git_hosting plugin:
+
+ rake selinux:install RAILS_ENV=production
+
+This will generate the redmine_git_hosting binaries in ./bin, install a selinux policy for these binaries (called
+redmine_git.pp), then install a complete context for redmine as follows:
+
+**(1)** Most of redmine will be marked with "public_content_rw_t".
+
+**(2)** The dispatch files in Rails.root/public/dispatch.* will be marked with "httpd_sys_script_exec_t"
+
+**(3)** The redmine_git_hosting binaries in Rails.root/vendor/plugins/redmine_git_hosting/bin will be labeled
+with "httpd_redmine_git_script_exec_t", which has been crafted to allow the sudo behavior required by these
+binaries.
+
+Note that this rake file has additional options. For instance, you can specify redmine roots with regular
+expressions (not globbed expessions!) as follows (notice the use of double quotes):
+
+ rake selinux:install RAILS_ENV=production ROOT_PATTERN="/source/.*/redmine"
+
+These additional options are documented in the selinux.rake file.
+
+Once this plugin is placed under selinux control, three of the redmine_git_hosting settings can
+no longer be modified from the settings page. They are: 'gitUser', 'gitoliteIdentityFile', and
+'gitoliteIdentityPublicKeyFile'. The plugin settings page will make this clear. The simplest way to
+modify these options is to temporarily place your system into permissive mode, refresh the setting page,
+change options, then place your system back into enforcing mode. Alternatively, you can alter the init.rb
+file and reinstall the plugin. Under normal operation, you will get one selinux complaint about /bin/touch
+in your log each time that you visit the plugin settings page.
+
+This rakefile and selinux configuration has been primarily tested on Redhat Enterprise Linux version 6.x
+with apache and fcgi. Other configurations may require slight tweaking.
+
## Tested Configurations
This plugin has been primarily tested on Ubuntu Server 10.10 and 11.04 (32 and 64 bit) with ChiliProject v1.x,
diff --git a/app/models/git_hosting_settings_observer.rb b/app/models/git_hosting_settings_observer.rb
index fee8d8a3f..fcd16e9ae 100644
--- a/app/models/git_hosting_settings_observer.rb
+++ b/app/models/git_hosting_settings_observer.rb
@@ -5,6 +5,8 @@ class GitHostingSettingsObserver < ActiveRecord::Observer
@@old_hook_asynch = Setting.plugin_redmine_git_hosting['gitHooksAreAsynchronous']
@@old_http_server = Setting.plugin_redmine_git_hosting['httpServer']
@@old_git_user = Setting.plugin_redmine_git_hosting['gitUser']
+ @@old_gitolite_identity = Setting.plugin_redmine_git_hosting['gitoliteIdentityFile']
+ @@old_gitolite_publickey = Setting.plugin_redmine_git_hosting['gitoliteIdentityPublicKeyFile']
@@old_repo_base = Setting.plugin_redmine_git_hosting['gitRepositoryBasePath']
@@ -14,12 +16,25 @@ def reload_this_observer
end
end
-
-
+ def before_save(object)
+ if object.name == "plugin_redmine_git_hosting" && !GitHosting.bin_dir_writeable?
+ # If bin directory not alterable, don't alow changes to
+ # Git Username, or Gitolite public or private keys
+ valuehash = object.value
+ valuehash['gitUser'] = @@old_git_user
+ valuehash['gitoliteIdentityFile'] = @@old_gitolite_identity
+ valuehash['gitoliteIdentityPublicKeyFile'] = @@old_gitolite_publickey
+ object.value = valuehash
+ end
+ end
+
def after_save(object)
if object.name == "plugin_redmine_git_hosting"
- %x[ rm -rf '#{ GitHosting.get_tmp_dir }' ]
+ if GitHosting.bin_dir_writeable?
+ %x[ rm -rf '#{ GitHosting.get_tmp_dir }' ]
+ %x[ rm -rf '#{ GitHosting.get_bin_dir }' ]
+ end
if @@old_repo_base != object.value['gitRepositoryBasePath']
GitHostingObserver.set_update_active(false)
@@ -49,6 +64,8 @@ def after_save(object)
@@old_hook_asynch = object.value['gitHooksAreAsynchronous']
@@old_http_server = object.value['httpServer']
@@old_git_user = object.value['gitUser']
+ @@old_gitolite_identity = object.value['gitoliteIdentityFile']
+ @@old_gitolite_publickey = object.value['gitoliteIdentityPublicKeyFile']
@@old_repo_base = object.value['gitRepositoryBasePath']
end
diff --git a/app/views/settings/_redmine_git_hosting.html.erb b/app/views/settings/_redmine_git_hosting.html.erb
index c6b1e7760..9e6527c84 100644
--- a/app/views/settings/_redmine_git_hosting.html.erb
+++ b/app/views/settings/_redmine_git_hosting.html.erb
@@ -26,19 +26,19 @@
diff --git a/config/locales/bg.yml b/config/locales/bg.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/bg.yml
+++ b/config/locales/bg.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/bs.yml b/config/locales/bs.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/bs.yml
+++ b/config/locales/bs.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/da.yml b/config/locales/da.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/el.yml b/config/locales/el.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/he.yml b/config/locales/he.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/id.yml b/config/locales/id.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index e9989cd0e..96dc0a6df 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -7,6 +7,9 @@ pt-BR:
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Ficheiro de identidade SSH Gitolite (Chave Privada)
label_git_repository_base_path: 'Directório Git base (Relativo à "home" do utilizador git)'
+ label_git_recycle_base_path: 'Directório Recycle Bin base (Relativo à "home" do utilizador git)'
+ label_git_recycle_expire_time: 'expirar o tempo para Recycle Bin (em horas)'
+ label_git_lock_wait_time: 'Sincronização de bloqueio o tempo de espera (em segundos)'
label_cannot_change_selinux: 'não pode ser mudada (selinux)'
field_git_daemon: Git Daemon
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index f6db9e33d..cf033316f 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -7,6 +7,9 @@ pt:
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Ficheiro de identidade SSH Gitolite (Chave Privada)
label_git_repository_base_path: 'Directório Git base (Relativo à "home" do utilizador git)'
+ label_git_recycle_base_path: 'Directório Recycle Bin base (Relativo à "home" do utilizador git)'
+ label_git_recycle_expire_time: 'expirar o tempo para Recycle Bin (em horas)'
+ label_git_lock_wait_time: 'Sincronização de bloqueio o tempo de espera (em segundos)'
label_cannot_change_selinux: 'não pode ser mudada (selinux)'
field_git_daemon: Git Daemon
diff --git a/config/locales/ro.yml b/config/locales/ro.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/ro.yml
+++ b/config/locales/ro.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/sl.yml b/config/locales/sl.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/sl.yml
+++ b/config/locales/sl.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index 723401ce1..b8c50fb92 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -6,7 +6,10 @@
label_git_user: Git Username
label_gitolite_identity_public_key_file: Gitolite SSH Identity File (Public Key)
label_gitolite_identity_file: Gitolite SSH Identity File (Private Key)
- label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_repository_base_path: Git Repository Base Path (Relative to git user home)
+ label_git_recycle_base_path: Git Recycle Bin Base Path (Relative to git user home)
+ label_git_recycle_expire_time: Expire Time for Repositories in Recycle Bin (in hours)
+ label_git_lock_wait_time: Synchronization Lock Wait Time (in seconds)
label_cannot_change_selinux: Cannot be changed under selinux
field_git_daemon: Git Daemon
diff --git a/db/migrate/20111123214911_add_settings_to_plugin.rb b/db/migrate/20111123214911_add_settings_to_plugin.rb
new file mode 100644
index 000000000..dc801779c
--- /dev/null
+++ b/db/migrate/20111123214911_add_settings_to_plugin.rb
@@ -0,0 +1,34 @@
+class AddSettingsToPlugin < ActiveRecord::Migration
+ def self.up
+ begin
+ # Add some new settings to settings page, if they don't exist
+ valuehash = (Setting.plugin_redmine_git_hosting).clone
+ valuehash['gitRecycleBasePath'] ||= 'recycle_bin/'
+ valuehash['gitRecycleExpireTime'] ||= '24.0'
+ valuehash['gitLockWaitTime'] ||= '10'
+ if (Setting.plugin_redmine_git_hosting != valuehash)
+ Setting.plugin_redmine_git_hosting = valuehash
+ say "Added redmine_git_hosting settings: 'gitRecycleBasePath', 'getRecycleExpireTime', 'getLockWaitTime'"
+ end
+ rescue
+ # ignore problems if plugin settings don't exist yet
+ end
+ end
+
+ def self.down
+ begin
+ # Remove above settings from plugin page
+ valuehash = (Setting.plugin_redmine_git_hosting).clone
+ valuehash.delete('gitRecycleBasePath')
+ valuehash.delete('gitRecycleExpireTime')
+ valuehash.delete('gitLockWaitTime')
+ if (Setting.plugin_redmine_git_hosting != valuehash)
+ Setting.plugin_redmine_git_hosting = valuehash
+ say "Removed redmine_git_hosting settings: 'gitRecycleBasePath', 'getRecycleExpireTime', 'getLockWaitTime'"
+ end
+ Setting.plugin_redmine_git_hosting = valuehash
+ rescue
+ # ignore problems if table doesn't exist yet....
+ end
+ end
+end
diff --git a/init.rb b/init.rb
index 721cdbaca..485b3dc77 100755
--- a/init.rb
+++ b/init.rb
@@ -12,11 +12,15 @@
description 'Enables Redmine / ChiliProject to control hosting of git repositories'
version '0.4.2'
url 'https://github.com/ericpaulbishop/redmine_git_hosting'
+
settings :default => {
- 'httpServer' => 'localhost',
- 'gitServer' => 'localhost',
- 'gitUser' => 'git',
+ 'httpServer' => "tessellation.cs.berkeley.edu/redmine",
+ 'gitServer' => 'tessellation.cs.berkeley.edu',
+ 'gitUser' => 'git-tess',
'gitRepositoryBasePath' => 'repositories/',
+ 'gitRecycleBasePath' => 'recycle_bin/',
+ 'gitRecycleExpireTime' => '24.0',
+ 'gitLockWaitTime' => '10',
'gitoliteIdentityFile' => RAILS_ROOT + '/.ssh/gitolite_admin_id_rsa',
'gitoliteIdentityPublicKeyFile' => RAILS_ROOT + '/.ssh/gitolite_admin_id_rsa.pub',
'allProjectsUseGit' => 'false',
diff --git a/lib/git_hosting.rb b/lib/git_hosting.rb
index 07aa7e0e3..829faa448 100755
--- a/lib/git_hosting.rb
+++ b/lib/git_hosting.rb
@@ -2,17 +2,34 @@
require 'net/ssh'
require 'tempfile'
require 'tmpdir'
+require 'stringio'
require 'gitolite_conf.rb'
+require 'gitolite_recycle.rb'
require 'git_adapter_hooks.rb'
module GitHosting
+ LOCK_WAIT_IF_UNDEF = 10 # In case settings not migrated (normally from settings)
+ REPOSITORY_IF_UNDEF = "repositories/" # In case settings not migrated (normally from settings)
+ # Used to register errors when pulling and pushing the conf file
+ class GitHostingException < StandardError
+ end
+
+ # Time in seconds to wait before giving up on acquiring the lock
+ def self.lock_wait_time
+ Setting.plugin_redmine_git_hosting['gitLockWaitTime'].to_i || LOCK_WAIT_IF_UNDEF
+ end
+
+ # Repository base path (relative to git user home directory)
+ def self.repository_base
+ Setting.plugin_redmine_git_hosting['gitRepositoryBasePath'] || REPOSITORY_IF_UNDEF
+ end
+
+ @@logger = nil
def self.logger
- # it may be useful to redefine this for some debugging purposes
- # but by default, we're just going to use the default Rails logger
- return Rails.logger
+ @@logger ||= MyLogger.new
end
@@web_user = nil
@@ -31,7 +48,6 @@ def self.git_user
Setting.plugin_redmine_git_hosting['gitUser']
end
-
@@mirror_pubkey = nil
def self.mirror_push_public_key
if @@mirror_pubkey.nil?
@@ -124,7 +140,7 @@ def self.repository_name project
end
def self.repository_path project
- return File.join(Setting.plugin_redmine_git_hosting['gitRepositoryBasePath'], repository_name(project)) + ".git"
+ return File.join(repository_base, repository_name(project)) + ".git"
end
def self.add_route_for_project(p)
@@ -162,7 +178,7 @@ def self.get_bin_dir
@@git_hosting_bin_dir ||=
Rails.root.join("vendor/plugins/redmine_git_hosting/bin")
if !File.directory?(@@git_hosting_bin_dir)
- logger.error "Creating bin directory: #{@@git_hosting_bin_dir}, Owner #{web_user}"
+ logger.info "Creating bin directory: #{@@git_hosting_bin_dir}, Owner #{web_user}"
%x[mkdir -p "#{@@git_hosting_bin_dir}"]
%x[chmod 750 "#{@@git_hosting_bin_dir}"]
%x[chown #{web_user} "#{@@git_hosting_bin_dir}"]
@@ -195,14 +211,14 @@ def self.bin_dir_writeable?(*option)
end
def self.git_exec_path
- return File.join(get_bin_dir(), "run_git_as_git_user")
+ return File.join(get_bin_dir, "run_git_as_git_user")
end
def self.gitolite_ssh_path
- return File.join(get_bin_dir(), "gitolite_admin_ssh")
+ return File.join(get_bin_dir, "gitolite_admin_ssh")
end
def self.git_user_runner_path
- return File.join(get_bin_dir(), "run_as_git_user")
+ return File.join(get_bin_dir, "run_as_git_user")
end
@@ -227,7 +243,7 @@ def self.git_user_runner
def self.update_git_exec
- logger.info "Setting up #{get_bin_dir()}"
+ logger.info "Setting up #{get_bin_dir}"
gitolite_key=Setting.plugin_redmine_git_hosting['gitoliteIdentityFile']
File.open(gitolite_ssh_path(), "w") do |f|
@@ -290,16 +306,14 @@ def self.update_git_exec
File.chmod(0550, git_exec_path())
File.chmod(0550, gitolite_ssh_path())
File.chmod(0550, git_user_runner_path())
- %x[chown #{web_user} -R "#{@@git_hosting_bin_dir}"]
+ %x[chown #{web_user} -R "#{get_bin_dir}"]
end
-
@@lock_file = nil
def self.lock(retries)
is_locked = false
- local_dir = get_tmp_dir()
if @@lock_file.nil?
- @@lock_file=File.new(File.join(local_dir,'redmine_git_hosting_lock'),File::CREAT|File::RDONLY)
+ @@lock_file=File.new(File.join(get_tmp_dir,'redmine_git_hosting_lock'),File::CREAT|File::RDONLY)
end
while retries > 0
@@ -318,195 +332,451 @@ def self.unlock
end
end
+ def self.shell(command)
+ begin
+ my_command = "#{command} 2>&1"
+ result = %x[#{my_command}].chomp
+ code = $?.exitstatus
+ rescue Exception => e
+ result=e.message
+ code = -1
+ end
+ if code != 0
+ logger.error "Command failed (return #{code}): #{command}"
+ logger.error "#{result}"
+ raise GitHostingException, "Shell Error"
+ end
+ end
+ # Try to get a cloned version of gitolite-admin repository.
+ #
+ # This code tries to recover from a variety of errors which have been observed
+ # in the field, including a loss of the admin key and an empty top-level directory
+ #
+ # Return: false => have uncommitted changes
+ # true => directory on master
+ #
+ # This routine must only be called after acquisition of the lock
+ #
+ # John Kubiatowicz, 11/15/11
def self.clone_or_pull_gitolite_admin
# clone/pull from admin repo
- local_dir = get_tmp_dir()
- if File.exists? "#{local_dir}/gitolite-admin"
- logger.info "Fetching changes for #{local_dir}/gitolite-admin"
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' fetch]
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' merge FETCH_HEAD]
- else
- logger.info "Cloning gitolite-admin repository"
- %x[env GIT_SSH=#{gitolite_ssh()} git clone #{git_user}@#{Setting.plugin_redmine_git_hosting['gitServer']}:gitolite-admin.git #{local_dir}/gitolite-admin]
- end
- %x[chmod 700 "#{local_dir}/gitolite-admin" ]
- # Make sure we have our hooks setup
- GitAdapterHooks.check_hooks_installed
+ repo_dir = File.join(get_tmp_dir,"gitolite-admin")
+ begin
+ if (File.exists? "#{repo_dir}") && (File.exists? "#{repo_dir}/.git") && (File.exists? "#{repo_dir}/keydir") && (File.exists? "#{repo_dir}/conf")
+ logger.info "Fetching changes from gitolite-admin repository to #{repo_dir}"
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' fetch]
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' merge FETCH_HEAD]
+
+ # unmerged changes=> non-empty return
+ return_val = %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' status --short].empty?
+ else
+ logger.info "Cloning gitolite-admin repository to #{repo_dir}"
+ shell %[rm -rf "#{repo_dir}"]
+ shell %[env GIT_SSH=#{gitolite_ssh()} git clone #{git_user}@#{Setting.plugin_redmine_git_hosting['gitServer']}:gitolite-admin.git #{repo_dir}]
+ return_val = true # on master, since fresh clone
+ end
+ shell %[chmod 700 "#{repo_dir}" ]
+ # Make sure we have our hooks setup
+ GitAdapterHooks.check_hooks_installed
+
+ return return_val
+ rescue
+ # Hm.... perhaps we have some other sort of failure...
+ logger.error "Failure to access gitolite-admin repository. Attempting to fix..."
+ begin
+ logger.info " Reestablishing gitolite key"
+ shell %[cat #{Setting.plugin_redmine_git_hosting['gitoliteIdentityPublicKeyFile']} | #{GitHosting.git_user_runner} 'cat > ~/id_rsa.pub']
+ shell %[#{GitHosting.git_user_runner} 'gl-setup ~/id_rsa.pub']
+ shell %[#{GitHosting.git_user_runner} 'rm ~/id_rsa.pub']
+
+ logger.info " Deleting and recloning gitolite-admin to #{repo_dir}"
+ shell %[rm -r #{repo_dir}] unless !File.exists?(repo_dir)
+ shell %[env GIT_SSH=#{gitolite_ssh()} git clone #{git_user}@#{Setting.plugin_redmine_git_hosting['gitServer']}:gitolite-admin.git #{repo_dir}]
+ shell %[chmod 700 "#{repo_dir}" ]
+ # Make sure we have our hooks setup
+ GitAdapterHooks.check_hooks_installed
+ logger.info "Successfully restablished access to gitolite-admin repository!"
+ rescue
+ logger.error "Failure again. Probably requires human intervention"
+ raise GitHostingException, "Gitolite-admine Clone Failure"
+ end
+ end
end
- def self.move_repository(old_name, new_name)
- old_path = File.join(Setting.plugin_redmine_git_hosting['gitRepositoryBasePath'], "#{old_name}.git")
- new_path = File.join(Setting.plugin_redmine_git_hosting['gitRepositoryBasePath'], "#{new_name}.git")
+ # Commit Changes to the gitolite-admin repository. This assumes that repository exists
+ # (i.e. that a clone_or_fetch_gitolite_admin has already be called).
+ #
+ # This routine must only be called after acquisition of the lock
+ #
+ # John Kubiatowicz, 11/15/11
+ def self.commit_gitolite_admin(*args)
+ resyncing = args && args.first
# create tmp dir, return cleanly if, for some reason, we don't have proper permissions
- local_dir = get_tmp_dir()
+ repo_dir = File.join(get_tmp_dir,"gitolite-admin")
+
+ # commit / push changes to gitolite admin repo
+ begin
+ if (!resyncing)
+ logger.info "Committing changes to gitolite-admin repository"
+ message = "Updated by Redmine"
+ else
+ logger.info "Committing corrections to gitolite-admin repository"
+ message = "Updated by Redmine: Corrections discovered during RESYNC_ALL"
+ end
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' add keydir/*]
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' add conf/gitolite.conf]
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' config user.email '#{Setting.mail_from}']
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' config user.name 'Redmine']
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' commit -a -m '#{message}']
+ shell %[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' push ]
+ rescue
+ logger.error "Problems committing changes to gitolite-admin repository!! Probably requires human intervention"
+ raise GitHostingException, "Gitlite-admin Commit Failure"
+ end
+ end
+ def self.move_repository(old_name, new_name)
#lock
- if !lock(5)
+ if !lock(lock_wait_time)
+ logger.error "git_hosting: move_repository() exited without acquiring lock!"
return
end
-
- # Make sure we have gitoite-admin cloned
- clone_or_pull_gitolite_admin
-
- # rename in conf file
- conf = GitoliteConfig.new(File.join(local_dir, 'gitolite-admin', 'conf', 'gitolite.conf'))
- conf.rename_repo( old_name, new_name )
- conf.save
+ begin
+ # Make sure we have gitoite-admin cloned
+ clone_or_pull_gitolite_admin
- # physicaly move the repo BEFORE committing/pushing conf changes to gitolite admin repo
- %x[#{git_user_runner} 'mkdir -p "#{new_path}"']
- %x[#{git_user_runner} 'rmdir "#{new_path}"']
- %x[#{git_user_runner} 'mv "#{old_path}" "#{new_path}"']
+ old_path = File.join(repository_base, "#{old_name}.git")
+ new_path = File.join(repository_base, "#{new_name}.git")
+
+ logger.warn "Adjusting position of repository from '#{old_name}' to '#{new_name}' in gitolite.conf"
+
+ # rename in conf file
+ conf = GitoliteConfig.new(File.join(get_tmp_dir, 'gitolite-admin', 'conf', 'gitolite.conf'))
+ conf.rename_repo( old_name, new_name )
+ conf.save
+
+ logger.warn " Moving repository from '#{old_path}' to '#{new_path}' in gitolite repository"
+
+ # physicaly move the repo BEFORE committing/pushing conf changes to gitolite admin repo
+ prefix = new_name[/.*(?=\/)/] # Complete directory path (if exists) without trailing '/'
+ if prefix
+ # Has subdirectory. Must construct destination directory
+ repo_prefix = File.join(repository_base, prefix)
+ GitHosting.shell %[#{git_user_runner} mkdir -p '#{repo_prefix}']
+ end
+ shell %[#{git_user_runner} 'mv "#{old_path}" "#{new_path}"']
+
+ # If any empty directories left behind, try to delete them. Ignore failure.
+ old_prefix = old_name[/.*?(?=\/)/] # Top-level old directory without trailing '/'
+ if old_prefix
+ repo_subpath = File.join(repository_base, old_prefix)
+ result = %x[#{GitHosting.git_user_runner} find '#{repo_subpath}' -type d ! -regex '.*\.git/.*' -empty -depth -delete -print].chomp.split("\n")
+ result.each { |dir| logger.warn " Removing empty repository subdirectory: #{dir}"}
+ end
+
+ # Commit / push changes to gitolite admin repo
+ commit_gitolite_admin
+
+ rescue GitHostingException
+ logger.error "git_hosting: move_repository() failed"
+ rescue => e
+ logger.error e.message
+ logger.error e.backtrace[0..4].join("\n")
+ logger.error "git_hosting: move_repository() failed"
+ end
+ # unlock
+ unlock()
+ end
+
+ # Delete repository from specified project.
+ #
+ # We remove all redmine keys from the repository access rights.
+ # There are then three options:
+ #
+ # 1) The repo has non-redmine keys => we leave it alone
+ # 2) The repo has no keys left, but repository delete is not enabled
+ # => will leave repository alone with redmine_dummy_key
+ # 3) The repo has no keys left and repository delete is enabled
+ # => will delete repository
+ def self.delete_repository(project)
+ # Grab lock
+ if !lock(lock_wait_time)
+ logger.error "git_hosting: delete_repository() exited without acquiring lock!"
+ return
+ end
- # commit / push changes to gitolite admin repo
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' add keydir/*]
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' add conf/gitolite.conf]
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' config user.email '#{Setting.mail_from}']
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' config user.name 'Redmine']
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' commit -a -m 'updated by Redmine' ]
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' push ]
+ begin
+ # Make sure we have gitolite-admin cloned
+ clone_or_pull_gitolite_admin
- # unlock
+ repo_name = repository_name(project)
+
+ conf = GitoliteConfig.new(File.join(get_tmp_dir, 'gitolite-admin', 'conf', 'gitolite.conf'))
+
+ # Kill off redmine keys
+ conf.delete_redmine_keys repo_name
+
+ if Setting.plugin_redmine_git_hosting['deleteGitRepositories'] == "true"
+ if conf.repo_has_no_keys? repo_name
+ logger.warn "Deleting repository '#{repo_name}' from gitolite.conf"
+ conf.delete_repo repo_name
+ GitoliteRecycle.move_repository_to_recycle repo_name
+ else
+ logger.warn "Repository '#{repo_name}' not deleted from gitolite.conf (non-redmine keys present and preserved)"
+ end
+ else
+ logger.warn "Deleting all redmine keys for repository '#{repo_name}' from gitolite.conf"
+ end
+
+ conf.save
+
+ # Commit / push changes to gitolite admin repo
+ commit_gitolite_admin
+
+ rescue GitHostingException
+ logger.error "git_hosting: delete_repository() failed"
+ rescue => e
+ logger.error e.message
+ logger.error e.backtrace[0..4].join("\n")
+ logger.error "git_hosting: delete_repository() failed"
+ end
unlock()
-
end
+ # Update keys for all members of projects of interest
+ #
+ # This code is entirely self-correcting for keys owned by users of the specified
+ # projects. It should work regardless of the history of steps that got us here.
+ #
+ # Note that this code has changed from the original. Now, we look at all keys owned
+ # by users in the specified projects to make sure that they are still live. We
+ # do this with a single pass through the keydir and do not rely on the "inactive"
+ # status to tell us that a key should be deleted. The reason is that weird
+ # synchronization issues (not entirely understood) can cause phantom keys to get left
+ # in the keydir which can really mess up gitolite.
+ #
+ # Also, when performing :resync_all, if the 'deleteGitRepositories' setting is 'true',
+ # then we will remove repositories in the gitolite.conf file that are identifiable as
+ # "redmine managed" (because they have one or more keys of the right form) but which
+ # are nolonger live for some reason (probably because the project was deleted).
+ #
+ # John Kubiatowicz, 11/15/11
+ #
+ # Usage:
+ #
+ # 1) update_repositories(project) => update for specified project
+ # 2) update_repositories([list of projects]) => update all projects
+ # 3) update_repositories(:flag1=>true, :flag2 => false)
+ #
+ # Current flags:
+ # :resync_all => go through all redmine-maintained gitolite repos,
+ # clean up keydir, delete unused keys, clean up gitolite.conf
@@recursionCheck = false
- def self.update_repositories(projects, is_repo_delete)
-
+ def self.update_repositories(*args)
+ flags = {}
+ args.each {|arg| flags.merge!(arg) if arg.is_a?(Hash)}
+ if flags[:resync_all]
+ logger.info "Executing RESYNC_ALL operation on gitolite configuration"
+ projects = Project.active.has_module(:repository).find(:all, :include => :repository)
+ else
+ projects = args.flatten.select{|p| p.is_a?(Project)}
+ end
+ git_projects = projects.uniq.select{|p| p.repository.is_a?(Repository::Git) }
+ return if git_projects.empty?
if(defined?(@@recursionCheck))
if(@@recursionCheck)
+ # This shouldn't happen any more -- log as error
+ logger.error "git_hosting: update_repositories() exited with positive recursionCheck flag!"
return
end
- end
+ end
@@recursionCheck = true
- logger.debug "Updating repositories..."
- projects = (projects.is_a?(Array) ? projects : [projects])
-
-
- # Don't bother doing anything if none of the projects we've been handed have a Git repository
- unless projects.detect{|p| p.repository.is_a?(Repository::Git) }.nil?
-
-
+ # Grab actual lock
+ if !lock(lock_wait_time)
+ logger.error "git_hosting: update_repositories() exited without acquiring lock!"
+ @@recursionCheck = false
+ return
+ end
- #lock
- if !lock(5)
- @@recursionCheck = false
- return
+ begin
+ # Make sure we have gitoite-admin cloned.
+ on_master = clone_or_pull_gitolite_admin
+
+ # Get directory for the gitolite-admin
+ repo_dir = File.join(get_tmp_dir,"gitolite-admin")
+
+ # Flag to indicate whether repo has changed. If we have uncommited changes, we will commit later.
+ changed = !on_master
+
+ # logger.info "Updating keydirectory for projects: #{git_projects.join ', '}"
+
+ keydir = File.join(repo_dir,"keydir")
+ old_keyhash = {}
+ Dir.foreach(keydir) do |keyfile|
+ user_token = GitolitePublicKey.ident_to_user_token(keyfile)
+ if !user_token.nil?
+ old_keyhash[user_token] ||= []
+ old_keyhash[user_token] << keyfile
+ end
+ end
+
+ git_projects.map{|proj| proj.member_principals.map(&:user).compact}.flatten.uniq.each do |cur_user|
+ active_keys = cur_user.gitolite_public_keys.active || []
+
+ # Remove old keys that happen to be left around
+ cur_token = GitolitePublicKey.user_to_user_token(cur_user)
+ old_keynames = old_keyhash[cur_token] || []
+ cur_keynames = active_keys.map{|key| "#{key.identifier}.pub"}
+ (old_keynames - cur_keynames).each do |keyname|
+ filename = File.join(keydir,"#{keyname}")
+ logger.warn "Removing gitolite key: #{keyname}"
+ %x[git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' rm keydir/#{keyname}]
+ changed = true
+ end
+
+ # Remove inactive keys (will already be deleted by above code)
+ cur_user.gitolite_public_keys.inactive.each {|key| GitolitePublicKey.destroy(key.id)}
+
+ # Add missing keys to the keydir
+ active_keys.each do |key|
+ keyname = "#{key.identifier}.pub"
+ unless old_keynames.index(keyname)
+ filename = File.join(keydir,"#{keyname}")
+ logger.info "Adding gitolite key: #{keyname}"
+ File.open(filename, 'w') {|f| f.write(key.key.gsub(/\n/,'')) }
+ changed = true
+ end
+ end
+
+ # In preparation for resync_all, below
+ old_keyhash.delete(cur_token)
+ end
+
+ # Remove keys for deleted users
+ if flags[:resync_all]
+ # All keys left in old_keyhash should be for users nolonger authorized for gitolite repos
+ old_keyhash.each_value do |keyname|
+ filename = File.join(keydir,"#{keyname}")
+ logger.warn "Removing orphan gitolite key: #{keyname}"
+ %x[git --git-dir='#{repo_dir}/.git' --work-tree='#{repo_dir}' rm keydir/#{keyname}]
+ changed = true
+ end
end
-
-
- # Make sure we have gitoite-admin cloned
- clone_or_pull_gitolite_admin
-
-
- local_dir = get_tmp_dir()
- conf = GitoliteConfig.new(File.join(local_dir, 'gitolite-admin', 'conf', 'gitolite.conf'))
+ conf = GitoliteConfig.new(File.join(repo_dir, 'conf', 'gitolite.conf'))
orig_repos = conf.all_repos
new_repos = []
new_projects = []
- changed = false
-
- projects.select{|p| p.repository.is_a?(Repository::Git)}.each do |project|
-
- repo_name = repository_name(project)
-
- #check for delete -- if delete we can just
- #delete repo, and ignore updating users/public keys
- if is_repo_delete
- if Setting.plugin_redmine_git_hosting['deleteGitRepositories'] == "true"
- conf.delete_repo(repo_name)
- end
- else
- #check whether we're adding a new repo
- if orig_repos[ repo_name ] == nil
- changed = true
- add_route_for_project(project)
- new_repos.push repo_name
- new_projects.push project
-
- end
-
-
- # fetch users
- users = project.member_principals.map(&:user).compact.uniq
- write_users = users.select{ |user| user.allowed_to?( :commit_access, project ) }
- read_users = users.select{ |user| user.allowed_to?( :view_changesets, project ) && !user.allowed_to?( :commit_access, project ) }
-
- # write key files
- users.map{|u| u.gitolite_public_keys.active}.flatten.compact.uniq.each do |key|
- filename = File.join(local_dir, 'gitolite-admin/keydir',"#{key.identifier}.pub")
- unless File.exists? filename
- File.open(filename, 'w') {|f| f.write(key.key.gsub(/\n/,'')) }
- changed = true
- end
- end
-
- # delete inactives
- users.map{|u| u.gitolite_public_keys.inactive}.flatten.compact.uniq.each do |key|
- filename = File.join(local_dir, 'gitolite-admin/keydir',"#{key.identifier}.pub")
- if File.exists? filename
- %x[git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' rm keydir/#{key.identifier}.pub]
- changed = true
- GitolitePublicKey.destroy(key.id)
- end
- end
-
- # update users
- read_user_keys = []
- write_user_keys = []
- read_users.map{|u| u.gitolite_public_keys.active}.flatten.compact.uniq.each do |key|
- read_user_keys.push key.identifier
- end
- write_users.map{|u| u.gitolite_public_keys.active}.flatten.compact.uniq.each do |key|
- write_user_keys.push key.identifier
- end
-
- #git daemon
- if (project.repository.extra.git_daemon == 1 || project.repository.extra.git_daemon == nil ) && project.is_public
- read_user_keys.push "daemon"
- end
-
- conf.set_read_user repo_name, read_user_keys
- conf.set_write_user repo_name, write_user_keys
+
+ # Regenerate configuration file for projects of interest
+ # logger.info "Updating gitolite.conf for projects: #{git_projects.join ', '}"
+ git_projects.each do |proj|
+ repo_name = repository_name(proj)
+
+ #check whether we're adding a new repo
+ if orig_repos[ repo_name ] == nil
+ changed = true
+ add_route_for_project(proj)
+ new_repos.push repo_name
+ new_projects.push proj
+
+ # Attempt to recover repository from recycle_bin, if present
+ GitoliteRecycle.recover_repository_if_present repo_name
+ end
+
+ # fetch users
+ users = proj.member_principals.map(&:user).compact.uniq
+ write_users = users.select{ |user| user.allowed_to?( :commit_access, proj ) }
+ read_users = users.select{ |user| user.allowed_to?( :view_changesets, proj ) && !user.allowed_to?( :commit_access, proj ) }
+
+ # update users
+ read_user_keys = []
+ write_user_keys = []
+ read_users.map{|u| u.gitolite_public_keys.active}.flatten.compact.uniq.each do |key|
+ read_user_keys.push key.identifier
+ end
+ write_users.map{|u| u.gitolite_public_keys.active}.flatten.compact.uniq.each do |key|
+ write_user_keys.push key.identifier
+ end
+
+ #git daemon
+ if (proj.repository.extra.git_daemon == 1 || proj.repository.extra.git_daemon == nil ) && proj.is_public
+ read_user_keys.push "daemon"
end
+
+ # Remove previous redmine keys, then add new keys
+ # By doing things this way, we leave non-redmine keys alone
+ conf.delete_redmine_keys repo_name
+ conf.add_read_user repo_name, read_user_keys
+ conf.add_write_user repo_name, write_user_keys
+
+ # This is in preparation for full resync (below)
+ orig_repos.delete repo_name
end
+
+ # If resyncing, check for orphan repositories which still have redmine keys...
+ # At this point, orig_repos contains all repositories in original gitolite.conf
+ # which are not part of an active redmine project. There are four possibilities:
+ #
+ # 1) These repos do not have redmine keys => we leave them alone
+ # 2) They have both redmine keys and other (non-redmine) keys => remove redmine keys
+ # 3) They have only redmine keys, but repository delete is not enabled
+ # => remove redmine keys (will leave redmine_dummy_key when we save)
+ # 4) They have only redmine keys and repository delete is enabled => delete repository
+ #
+ # Finally, delete expired files from recycle bin.
+ if flags[:resync_all]
+ orig_repos.each_key do |repo_name|
+ if conf.is_redmine_repo? repo_name
+ # First, delete redmine keys for this repository
+ conf.delete_redmine_keys repo_name
+ if (Setting.plugin_redmine_git_hosting['deleteGitRepositories'] == "true") && (conf.repo_has_no_keys? repo_name)
+ logger.warn "Deleting orphan repository '#{repo_name}' from gitolite.conf"
+ conf.delete_repo repo_name
+ GitoliteRecycle.move_repository_to_recycle repo_name
+ else
+ logger.info "Deleting redmine keys for orphan repository '#{repo_name}' from gitolite.conf"
+ end
+ end
+ end
+ GitoliteRecycle.delete_expired_files
+ end
if conf.changed?
conf.save
changed = true
end
-
+
if changed
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' add keydir/*]
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' add conf/gitolite.conf]
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' config user.email '#{Setting.mail_from}']
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' config user.name 'Redmine']
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' commit -a -m 'updated by Redmine' ]
- %x[env GIT_SSH=#{gitolite_ssh()} git --git-dir='#{local_dir}/gitolite-admin/.git' --work-tree='#{local_dir}/gitolite-admin' push ]
+ # Have changes. Commit / push changes to gitolite admin repo
+ commit_gitolite_admin flags[:resync_all]
end
-
+
# Set post recieve hooks for new projects
# We need to do this AFTER push, otherwise necessary repos may not be created yet
if new_projects.length > 0
GitAdapterHooks.setup_hooks(new_projects)
end
- unlock()
- end
- @@recursionCheck = false
+ rescue GitHostingException
+ logger.error "git_hosting: update_repositories() failed"
+ rescue => e
+ logger.error e.message
+ logger.error e.backtrace[0..4].join("\n")
+ logger.error "git_hosting: update_repositories() failed"
+ end
+ unlock()
+ @@recursionCheck = false
end
-
- def self.clear_cache_for_project(project)
+ def self.clear_cache_for_project(project)
if project.is_a?(Project)
project = project.identifier
end
@@ -539,5 +809,24 @@ def self.update_global_hook_params
unlock()
end
end
+
+ class MyLogger
+ # Prefix to error messages
+ ERROR_PREFIX = "***> "
+
+ # For errors, add our prefix to all messages
+ def error(*progname, &block)
+ if block_given?
+ Rails.logger.error(*progname) { "#{ERROR_PREFIX}#{yield}".gsub(/\n/,"\n#{ERROR_PREFIX}") }
+ else
+ Rails.logger.error "#{ERROR_PREFIX}#{progname}".gsub(/\n/,"\n#{ERROR_PREFIX}")
+ end
+ end
+
+ # Handle everything else with base object
+ def method_missing(m, *args, &block)
+ Rails.logger.send m, *args, &block
+ end
+ end
end
diff --git a/lib/git_hosting/patches/repositories_controller_patch.rb b/lib/git_hosting/patches/repositories_controller_patch.rb
index 72e71c999..e4935d9bd 100644
--- a/lib/git_hosting/patches/repositories_controller_patch.rb
+++ b/lib/git_hosting/patches/repositories_controller_patch.rb
@@ -42,7 +42,7 @@ def edit_with_scm_settings
end
end
- GitHosting.update_repositories(@project, false) if !@project.repository.nil?
+ GitHosting.update_repositories(@project) if !@project.repository.nil?
GitHosting.setup_hooks(@project) if !@project.repository.nil?
else
diff --git a/lib/git_hosting/patches/repository_patch.rb b/lib/git_hosting/patches/repository_patch.rb
index 6c3695250..0fc6cc62e 100644
--- a/lib/git_hosting/patches/repository_patch.rb
+++ b/lib/git_hosting/patches/repository_patch.rb
@@ -32,8 +32,8 @@ def fetch_changesets_with_disable_update
# Do actual update
fetch_changesets_without_disable_update
- # Reenable updates to perform a single update
- GitHostingObserver.set_update_active(true);
+ # Reenable updates to perform a sync of all projects
+ GitHostingObserver.set_update_active(:resync_all);
end
end
diff --git a/lib/git_hosting/patches/sys_controller_patch.rb b/lib/git_hosting/patches/sys_controller_patch.rb
index 6b35c86e5..cf473c949 100644
--- a/lib/git_hosting/patches/sys_controller_patch.rb
+++ b/lib/git_hosting/patches/sys_controller_patch.rb
@@ -8,8 +8,8 @@ def fetch_changesets_with_disable_update
# Do actual update
fetch_changesets_without_disable_update
- # Reenable updates to perform a single update
- GitHostingObserver.set_update_active(true);
+ # Perform the updating process on all projects
+ GitHostingObserver.set_update_active(:resync_all);
end
def self.included(base)
diff --git a/lib/gitolite_conf.rb b/lib/gitolite_conf.rb
index 1a57a3e88..3279ff7e8 100755
--- a/lib/gitolite_conf.rb
+++ b/lib/gitolite_conf.rb
@@ -1,10 +1,16 @@
module GitHosting
class GitoliteConfig
+ DUMMY_REDMINE_KEY="redmine_dummy_key"
+
def initialize file_path
@path = file_path
load
end
+ def logger
+ return GitHosting.logger
+ end
+
def save
File.open(@path, "w") do |f|
f.puts content
@@ -39,6 +45,29 @@ def rename_repo old_name, new_name
end
end
+ # A repository is a "redmine" repository if it has redmine keys or no keys
+ # (latter case is checked, since we end up adding the DUMMY_REDMINE_KEY to
+ # a repository with no keys anyway....
+ def is_redmine_repo? repo_name
+ repository(repo_name).rights.detect {|perm, users| users.detect {|key| is_redmine_key? key}} || (repo_has_no_keys? repo_name)
+ end
+
+ def delete_redmine_keys repo_name
+ return if !@repositories[repo_name]
+
+ repository(repo_name).rights.each do |perm, users|
+ users.delete_if {|key| is_redmine_key? key}
+ end
+ end
+
+ def repo_has_no_keys? repo_name
+ !repository(repo_name).rights.detect {|perm, users| users.length > 0}
+ end
+
+ def is_redmine_key? keyname
+ (GitolitePublicKey::ident_to_user_token(keyname) || keyname == DUMMY_REDMINE_KEY) ? true : false
+ end
+
def changed?
@original_content != content
end
@@ -51,7 +80,6 @@ def all_repos
return repos
end
-
private
def load
@original_content = []
@@ -81,14 +109,15 @@ def repository repo_name
def content
content = []
- # To facilitate creation of repos, even when no users are defined
- # always define at least one user -- specifically the admin
- # user which has rights to modify gitolite-admin and control
- # all repos. Since the gitolite-admin user can grant anyone
- # any permission anyway, this isn't really a security risk.
- # If no users are defined, this ensures the repo actually
- # gets created, hence it's necessary.
- admin_user = @repositories["gitolite-admin"].rights["RW+".to_sym][0]
+ # If no gitolite-admin user, something seriously wrong. Add it in with id_rsa.
+ #
+ # If this doesn't work for some reason, will be corrected at later time by
+ # gl-setup run.
+ if @repositories["gitolite-admin"].nil?
+ content << "repo\tgitolite-admin"
+ content << "tRW+\t=\tid_rsa"
+ content << ""
+ end
@repositories.each do |repo, rights|
content << "repo\t#{repo}"
has_users=false
@@ -99,7 +128,8 @@ def content
end
end
if !has_users
- content << "\tR\t=\t#{admin_user}"
+ # If no users, use dummy key to make sure repo created
+ content << "\tR\t=\t#{DUMMY_REDMINE_KEY}"
end
content << ""
end
diff --git a/lib/gitolite_recycle.rb b/lib/gitolite_recycle.rb
new file mode 100755
index 000000000..e140054d2
--- /dev/null
+++ b/lib/gitolite_recycle.rb
@@ -0,0 +1,112 @@
+module GitHosting
+ # This class implements a basic recycle bit for repositories deleted from the gitolite repository
+ #
+ # Whenever repositories are deleted, we rename them and place them in the recycle_bin.
+ # Assuming that GitoliteRecycle.delete_expired_files is called regularly, files in the recycle_bin
+ # older than 'preserve_time' will be deleted. Both the path for the recycle_bin and the preserve_time
+ # are settable as settings.
+ #
+ # John Kubiatowicz, 11/21/11
+ class GitoliteRecycle
+ TRASH_DIR_SEP = "__" # Separator character(s) used to replace '/' in name
+
+ RECYCLE_BIN_IF_UNDEF = "recycle_bin/" # In case settings not migrated (normally from settings)
+ PRESERVE_TIME_IF_UNDEF = 1440 # In case settings not migrated (normally from settings)
+
+ def self.logger
+ return GitHosting.logger
+ end
+
+ # Repository base path (relative to git user home directory)
+ def self.repository_base
+ Setting.plugin_redmine_git_hosting['gitRepositoryBasePath']
+ end
+
+ # Recycle bin base path (relative to git user home directory)
+ def self.recycle_bin
+ Setting.plugin_redmine_git_hosting['gitRecycleBasePath'] || RECYCLE_BIN_IF_UNDEF
+ end
+
+ # Recycle preservation time (in minutes)
+ def self.preserve_time
+ (Setting.plugin_redmine_git_hosting['gitRecycleExpireTime'].to_f * 60).to_i || PRESERVE_TIME_IF_UNDEF
+ end
+
+ # Scan through the recyclebin and delete files older than 'preserve_time' minutes
+ # This can fail silently if, for instance, the recycle_bin doesn't exist. That is the intended behavior.
+ def self.delete_expired_files
+ result = %x[#{GitHosting.git_user_runner} 'find #{recycle_bin} -type d -regex '.*\.git' -cmin +#{preserve_time} -prune -print'].chomp.split("\n")
+ if result.length > 0
+ logger.warn "Garbage-collecting expired file#{(result.length != 1) ? "s" : ""} from recycle bin:"
+ result.each do |filename|
+ begin
+ GitHosting.shell %[#{GitHosting.git_user_runner} rm -r #{filename}]
+ logger.warn " Deleting #{filename}"
+ rescue
+ logger.error "GitoliteRecycle.delete_expired_files() failed trying to delete repository #{filename}!"
+ end
+ end
+
+ # Optionally remove recycle_bin (but only if empty). Ignore error if non-empty
+ %x[#{GitHosting.git_user_runner} rmdir #{recycle_bin}]
+ end
+ end
+
+ def self.move_repository_to_recycle repo_name
+ repo_path = File.join(repository_base, repo_name)
+ new_path = File.join(recycle_bin,"#{Time.now.to_i.to_s}#{TRASH_DIR_SEP}#{name_to_recycle_name(repo_name)}")
+ begin
+ GitHosting.shell %[#{GitHosting.git_user_runner} mkdir -p '#{recycle_bin}']
+ GitHosting.shell %[#{GitHosting.git_user_runner} mv '#{repo_path}.git' '#{new_path}.git']
+ logger.warn " Moving '#{repo_name}.git' from gitolite repository => '#{new_path}.git'. Will remain for at least #{preserve_time/60.0} hours"
+ # If any empty directories left behind, try to delete them. Ignore failure.
+ old_prefix = repo_name[/.*?(?=\/)/] # Top-level old directory without trailing '/'
+ if old_prefix
+ repo_subpath = File.join(repository_base, old_prefix)
+ result = %x[#{GitHosting.git_user_runner} find '#{repo_subpath}' -type d ! -regex '.*\.git/.*' -empty -depth -delete -print].chomp.split("\n")
+ result.each { |dir| logger.warn " Removing empty repository subdirectory: #{dir}"}
+ end
+ rescue
+ logger.error "Attempt to move repository '#{repo_name}.git' to recycle bin failed"
+ end
+ end
+
+ def self.recover_repository_if_present repo_name
+ # Pull up any matching repositories. Sort them (beginning is representation of time)
+ myregex = File.join(recycle_bin,"[0-9]+#{TRASH_DIR_SEP}#{name_to_recycle_name(repo_name)}.git")
+ files = %x[#{GitHosting.git_user_runner} find '#{recycle_bin}' -type d -regex '#{myregex}' -prune].chomp.split("\n").sort {|x,y| y <=> x }
+ if files.length > 0
+ # Found something!
+ logger.warn "Restoring '#{repo_name}.git' to gitolite repository from recycle bin (#{files.first})"
+ begin
+ prefix = repo_name[/.*(?=\/)/] # Complete directory path (if exists) without trailing '/'
+ if prefix
+ repo_prefix = File.join(repository_base, prefix)
+ # Has subdirectory. Must reconstruct directory
+ GitHosting.shell %[#{GitHosting.git_user_runner} mkdir -p '#{repo_prefix}']
+ end
+ repo_path = File.join(repository_base, repo_name)
+ GitHosting.shell %[#{GitHosting.git_user_runner} mv '#{files.first}' '#{repo_path}.git']
+
+ # Optionally remove recycle_bin (but only if empty). Ignore error if non-empty
+ %x[#{GitHosting.git_user_runner} rmdir #{recycle_bin}]
+ return true
+ rescue
+ logger.error "Attempt to recover '#{repo_name}.git' failed"
+ return false
+ end
+ else
+ false
+ end
+ end
+
+ # This routine takes a name and turns it into a name for the recycle bit,
+ # where we have a 1-level directory full of deleted repositories which
+ # we keep for 'preserve_time'.
+ def self.name_to_recycle_name repo_name
+ new_trash_name = "#{repo_name}".gsub(/\//,"#{TRASH_DIR_SEP}")
+ end
+
+
+ end
+end
diff --git a/tasks/gitolite.rake b/tasks/gitolite.rake
index e15d136d3..8b3b1715f 100644
--- a/tasks/gitolite.rake
+++ b/tasks/gitolite.rake
@@ -1,11 +1,26 @@
+# There are two tasks here of interest: gitolite:update_repositories and gitolite:fetch_changesets.
+# The second includes the first (since fetching of changesets causes updating of gitolite config).
+#
+# As of the most recent release, either of these will complete resynchronize the gitolite configuration
+# and can thus be used to recover from errors that might have been introduced by sychronization errors.
+#
+# Specifically:
+#
+# 1) Resynchronize gitolite configuration (fix keys directory and configuration). Also, expire
+# repositories in the recycle_bin if time.
+#
+# rake gitolite:update_repositories RAILS_ENV=xxx
+#
+# 2) Fetch all changesets for repositories and then rescynronize gitolite configuration (as in #1)
+#
+# rake gitolite:fetch_changes RAILS_ENV=xxx
+#
namespace :gitolite do
- desc "update gitolite repositories"
+ desc "Update all gitolite repositories"
task :update_repositories => [:environment] do
- projects = Project.active
- puts "Updating repositories for projects #{projects.join(' ')}"
- GitHosting.update_repositories(projects, false)
+ GitHosting.update_repositories(:resync_all => true)
end
- desc "fetch commits from gitolite repositories"
+ desc "Fetch commits from gitolite repositories/update gitolite configuration"
task :fetch_changes => [:environment] do
Repository.fetch_changesets
end
From 01e8b32412eaceefe87bfdbbf51448f1b6bdff82 Mon Sep 17 00:00:00 2001
From: John Kubiatowicz
Date: Sun, 27 Nov 2011 09:49:47 -0800
Subject: [PATCH 05/11] Missed a performance case/source of recursion
complaints in the log.
This is not a major issue (or even correctness issue), but will cause
complaints of recursion in the log.
---
app/controllers/gitolite_public_keys_controller.rb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/controllers/gitolite_public_keys_controller.rb b/app/controllers/gitolite_public_keys_controller.rb
index 1276f8471..131d2af40 100644
--- a/app/controllers/gitolite_public_keys_controller.rb
+++ b/app/controllers/gitolite_public_keys_controller.rb
@@ -18,12 +18,14 @@ def delete
end
def update
+ GitHostingObserver.set_update_active(false)
if @gitolite_public_key.update_attributes(params[:public_key])
flash[:notice] = l(:notice_public_key_updated)
redirect_to url_for(:controller => 'my', :action => 'account')
else
render :action => 'edit'
end
+ GitHostingObserver.set_update_active(true)
end
def create
From fffa1fd579161ffb4071f975b4476120f1fcdd4b Mon Sep 17 00:00:00 2001
From: John Kubiatowicz
Date: Tue, 3 Jan 2012 21:05:53 -0800
Subject: [PATCH 06/11] Update of selinux policy for latest version of selinux
package. Also change rakefile to install binary policy (redmine_git.pp)
instead of compiling source policy (redmine_git.te) before every
installation. New rake task added to recompile policy if necessary:
rake selinux:redmine_git_hosting:build_policy
---
selinux/README | 14 +++++++++++---
selinux/redmine_git.pp | Bin 127822 -> 128153 bytes
selinux/redmine_git.te | 10 ++++++++--
tasks/selinux.rake | 10 ++++++++--
4 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/selinux/README b/selinux/README
index a877fb20b..b57a919ec 100644
--- a/selinux/README
+++ b/selinux/README
@@ -52,15 +52,23 @@ Somewhat less far-reaching options include:
# install new selinux policy, and install selinux context for
# the redmine_git_hosting plugin
- rake selinux:redmine_git_hosting:install
+ rake selinux:redmine_git_hosting:install RAILS_ENV=production
-Finally, for those who are hand-crafting their own file context:
+For those who are hand-crafting their own file context:
# Build bin directory with customized scripts for redmine_git_hosting
# and install new selinux policy. No file contexts will be
# installed (so that you must do customization afterwards).
- rake selinux:redmine_git_hosting:install_scripts_and_policy
+ rake selinux:redmine_git_hosting:install_scripts_and_policy RAILS_ENV=production
+
+Finally, to rebuild the policy file (redmine_git.pp) from source (redmine_git.te),
+you can type:
+
+ # Rebuild redmine_git_hosting selinux policy from source
+
+ rake selinux:redmine_git_hosting:build_policy RAILS_ENV=production
+
diff --git a/selinux/redmine_git.pp b/selinux/redmine_git.pp
index bfe21e6945a9e61a536b6baf50f119f2006732a9..80895f79f0f1bb1e8a076256f6c605b863778ce1 100644
GIT binary patch
delta 4706
zcmb7He^k^}7M}|;4loSE4~GF}U`*UF1KlDmrD28{hWW*SyPH4QWccZ(61Xt6*0xBe
z&34<G)`np=Lk895pE?b?Gu+J0?R^q1N_of8M!e^{HoMBc&k-(Ok
z;}J?`Dt%See6A!iRf_diI%4pUISytC?xa0_U7-R>Xri0&)^ArGr8H^R@jnY_+Ni=2!NCDZYkB?@j4zD6txmRJDEXfP|3^|jvSMkxw+
z2S`O`iuQv4zBU`ds#)f62Paf0pf+Gh%
zDpup};<<|7d3|aEZ=fyd&}mPCslv$vb~B!ktI=Rl;z@flsx3N{JCYzC2c{;)Q^xT
z#}xw^9FA^&pt;6Z@AWCL)s%_9cPU|-P~YNOh@Bo??DB@?J}EmhDBU{DcPrp_p;76M
zg)&aXigPZ`_k1Ct!#G*8naMXD-P|N`dM=IO1>u`Q>o4W&XR{s!c~rXCAxVRgR#Nm7
zQhIWP%O^?_z(#wU#@0Uy2~oTjVugU`x}GOWN02%MQF@d(+oJ-zK*~Lt;N-KZ#bJ{U
zQVbbP5#nQ{m`Gwhe(!08Xnv%y<242Q$6C-}SLs4|C?pYZK1il7>&N(}EEj(Mq08-F?XXIjqhu*
z_lOH$Kaz^75f5rNk{)@~Y}AH$zUH7Hq|jc1DH~HdJuhk_D3&zM>u&UKGvN8nR>5a%
z%EFG+RklT)RI9*m!fmv6(s~U6u9EwI*jR>Xc5z;#cf+Y!^ih#
zVc%90jXJ#PuXZsuyD~vVX7WOkiLr1)VQ~2eh}B=t8WzJ
z(%q>j?{v}l2YmIAWV3X)9!K94GUkt_uFaiZ1@)MAP#40Y7s(bc*)gL5i7HK+F`?ZLtm#0RLEeClzc;ux~;!}Bg^3nJ%
z?Og~`DTm_?QnBH^iTs?yE_P=Gq~hpd1#UiU!P6V{7;&Unlxf9!H{O4=8h0E?L*w6T
zMfv{WO+5}Cx8o-tXenZDG4e8&co#l#NROT&8ca7e_AZ-O?+Z;NTzGE&IzkF@Z$!JGRvH;G1c
z9OK0a6&hrv3f(HIf_G0|lg5j@CheWZi|+wkf5X-I_*pFu{HFx}ayA9~&bjcR0TN5>)gsm$5Mw%*2i>h3vyPN_%b`)QSS}oT}yrj{`0i9S;wx8ZUe|
zHWnZJy|^5BCLYd3O`83*OwRWEAdijCq*qh!SMp~yWUz8Ny*!p_pi0&F)wNa!qR>!~
z%sO;%nC*(8w{iDH1;2$^M-1rLH+pXL^-mZXv&d*9kBlyzcM5{7oz4xu`w0V@Wi_+i
zOb;*~j@*q3mXSk`?;kK!U$|$;gt=R?*ia5E`H|gTUH59{&m)h)x)gTCz*P2W4%~fx
zJL=*|Hm^`pnZjCZAZITWf`dibU@DVa;CWCm
z^9o3oMEfl8QV1YVa<6_VRX)h$WH
z7BVVAk~t(Pl_X*#=PSrMt(?5gBT0!Q;Q@fTEVBY?S6F
==U|9jpk6&d{r=We05!3bD2>Ono1AG$^|L(6A;JJQ{Z6Wt!hY?gC#(}O_PCj
z-%GK(u$)dhQ7QaMIZXcos{Um5{tAej2)6$S=;{3dn9CkpNwF86KVM1F6_leMLT^Or
zmN|1i4F@kf*|+tSg>QJ$ha!W@IJ#ZIiWKQ$#FDi6P7_)^Qz+8(lPQ)IAv#2d)p=w)
zJ9e`d_dpIs#ZS~w7PRVI2h0zw5S#-y<$!qnhFKBOqim<$eedPhrqr|fNBZvfz3<-p
z?t8y({X8xHNm|bDhVS?uR4SF4ev|08^`Cok?p=&Wt1Y;9aW>RqcC|S-yso7+bZgVv
z(ArzVp@@dWuEt)M87JB@K!;0g)_m2JiWDNNR^Hg&7K+@qrY)TilVqtXI7ijCzO6;D
zG6|W7`yEC4_NL~x_O{kggi9|FCOdtp7`LV4Zr2q2w=Ekc2kkw4SM)MSn==Ql5<3n%^C63el**Hc
zPdbZXwotb@H5rlBYeEqnS-tqHLq?jZM6{|SQZGnO$EDP7WCxV?*GQ(|=P)kR6CF$T7-Z}Wb+d#5kd1n!n
zuKh(QVjxqcv?~kxJh^C~M!C&n$D0EdO8g`!7G^cxT)0#~^SpLE>z%J{3Pp-YF_SFK
z(^Bp@!p$xMAFmTG_32=d(CGB#z!b5WsILI75ws6|c^C^?kWxay>)6D5APr~vGoXmG
zv)0|Vt|ihQifGB`T%mBgUk8^6Xs7>L$mUSyjo}+3vOLpx5phwC7OoI7jWwAF0ZXRn
zbJ5=m1)Z8PO=^_!4Nq9NmN0zZHCYqMBUeEY`OH8DWD4;_bd)HqtjSD4zAumsPTJ69
zJP^o(bVW3yofpPJcJvB~?+q)0dTg95W1}ujKK`An{zmJQCESno{z8@NUWl
zwGXS*ftC$zf_!{&$+h4Y!7Z%44C;jZuWPeFBht#l?5j0ErJ#5Ew0NdA7p4kcVVxeo
zq0&0)a`7H#3HEH!#_p{9#>TW(D8#OA6E*GZZU=t(Co}eJYsLQK0X(zKNN_Kn-ewZC
z;*SHk?Sv8IScB?gCX$gW#~(l5N^Xhp>i_rdK2PuzfmJG=>4
z@{QY#N%n(i+MX|La=(gyZl`015zDvNV9ucy^uAey(XC$m@-w6G!Phtj1kQbp;%4EG
z@L2F6lbR>kg2nL?tbAk|_UtOb(~mgVU^z^QJ-_n?$&M|2+yGp#1mou{7=6Oj?Mr3x
zOqhnzogR*3*^?j9YQeInIBvoI^I~PesWhohK4u!DwD9qg1djcW%*6g(icK7Ru>v#5
zT(XOSb0utO7EQ`eo_-at+h@jTuUD+`7+$+Gx$*+f{?bFPVtw!Hq9j6s85g{n|r_iFFz2(mSNr(q)s1
zl~3)(jF$xiSH1jEiYnEBbB+{HLkY7A9*h5ZKS;;u#pBPn($9?F{?(7A`8h
zp4eW1WrzF;+(U=3ncb64yR7_M4Q|+O!mdMfn(5?oIF3HajP`1AFkU4Q-;q&CK2I*z
z`XNi)YX5Pc>;&sE_lOcV(D0TC8;(ATuA@`1^6xe=sCWp;gCluxsgGGuf6DU%az*&%
z4)-`c{U0niwAmBu|Hm_c@pl`=l;V?4nQ%0n)SZC6&&;PGJ5vn7@TpeJI5j#vFA^U&
zJTI2rxaA;j+|o(|Fo44}Kxc1ljaff95A-~#eYZJ<-eGvx=4Q26^0<@)n~DEcype60
zN+pS-mPO0KMl;TY;m35bji25O82ZkLn$LHOGpUq(e9O4lT{Co@1gg)NS$QTsXB=k=DY=Wr
zQ&}`svPb3kIWOJ+v+<_rQm$uUmW)dfYs|jLR80#N$lP%dS25};Ty5B
zGCgGQ?#aUEVhdP*I+QW(L}~<{4GyhLRMsb2Op@ms;CB7;fBk28I89~a<6Je=2PSxR
z0*ZZ}*WpcdSt(NtDeU1}VLfYDPH&qsBR%~uf6*+iO7@@;mg$d=v#?XcVja}TbiJ}Y
z9bjRXSfNS3{EIQ3?zaNTtvAveab%Ep&*y|f_Q$DQesGL@>7`trzk%4vS&6)iLdv13
zk|~Vt)Uuq}6x{gj)oIwkJh*zyQLB>J|JJI%3f8dx#q|D2be2wFJeM9xFOG|Tk#v$$
z&R&q6wNcn%JNVgsHYi{jb~wcJc5t(gt00f9vcnPyRoW=W?{LQVZpz#CDl)yoLm~~;
z#OSFahPN7uB=j36m|1Hzc)4&MJ5Wtl2r{#o4tSWYaFN_;2khXGFrZ8I{HnM{e
z)J)@o>$r6@J4i-G>6LCtPbk&6DTq7#kk59xAt)ieNBYWRYe%4W5|EkMyoyGTSJ6=W
z6lf)fPWd5+z2>7j2&l}jKwTW_3=sF6ADTJT$_z`$dAx?y1aw(IfmV&sE{xC`f(n;%
zreOzz^!?YlIUjqzl27HooSocvVF|Q>?2X}J-uj>#V$}MjeO+~MzlNP@qNFTbN}1ig
zjG!J4J-wVD^)iA!T}seO4*5BEK!IQ`+p&~(Zsq*$dZ=Ztu4jxFw(#6J1{vUwL>F1N;RnmCZ^@^Kw3CPOoSAat*rE{gZ^b4>Z^-#@v
zR*=7dVn9TEcLPPFzaA_T=8>GA9a#n;<_jf=FrQf)poQf$5@DbLENKf#sz$LO^6(z<
zlH6TE0$%Bkn@tmVVvD%;DGSpykCDHUl*yu-pp-*KddAZWd>ReINo++sWQ`$qJ^KU1Xzw`UOtz;ToYFFX
z?z4i`R2t#vmeusdrrZSVbeMe8+4izv#~5fr2hq3!ZtU0M+fU3DH;0ru+`$J|(m>od
U%tvY#^m%Uv>xRBfXCNp2zdwhul>h($
diff --git a/selinux/redmine_git.te b/selinux/redmine_git.te
index 54b0a7aa1..36c73d2d8 100644
--- a/selinux/redmine_git.te
+++ b/selinux/redmine_git.te
@@ -8,10 +8,13 @@ require {
type httpd_t, httpd_sys_script_t, httpd_sys_script_exec_t;
type sudo_db_t;
type httpd_redmine_git_script_t;
+ type httpd_redmine_git_script_exec_t;
+ type gitosis_var_lib_t;
class process { setrlimit setfscreate };
class netlink_route_socket { write getattr read bind create nlmsg_read };
class capability { setuid sys_resource setgid };
- class dir { getattr search write};
+ class dir { getattr search write write rename create reparent rmdir };
+ class lnk_file unlink;
}
apache_content_template(redmine_git)
@@ -51,10 +54,13 @@ allow httpd_redmine_git_script_t self:netlink_route_socket { write getattr read
allow httpd_redmine_git_script_t self:process setrlimit;
allow httpd_redmine_git_script_t sudo_db_t:dir { getattr search };
+# Capabilities required to manage gitolite repositories
+allow httpd_redmine_git_script_t gitosis_var_lib_t:dir { rename create reparent rmdir };
+allow httpd_redmine_git_script_t gitosis_var_lib_t:lnk_file unlink;
gitosis_read_lib_files(httpd_redmine_git_script_t)
gitosis_manage_lib_files(httpd_redmine_git_script_t)
-httpd_rw_stream_sockets(httpd_redmine_git_script_t)
+apache_rw_stream_sockets(httpd_redmine_git_script_t)
kernel_read_kernel_sysctls(httpd_redmine_git_script_t)
logging_send_syslog_msg(httpd_redmine_git_script_t)
diff --git a/tasks/selinux.rake b/tasks/selinux.rake
index 92e1e4c7c..3eae4cfd5 100644
--- a/tasks/selinux.rake
+++ b/tasks/selinux.rake
@@ -153,8 +153,14 @@ namespace :selinux do
desc "Install selinux tags and policy for redmine_git_hosting."
task :install_policy => [:environment] do
puts "[Installing selinux tags and policy for redmine_git_hosting:"
- plugin_roots = redmine_roots("vendor/plugins/redmine_git_hosting")
- sh "#{plugin_roots[0]}/selinux/redmine_git.sh"
+ sh "semodule -i #{Rails.root}/vendor/plugins/redmine_git_hosting/selinux/redmine_git.pp"
+ puts "DONE.]"
+ end
+
+ desc "Build and install selinux tags and policy for redmine_git_hosting."
+ task :build_policy => [:environment] do
+ puts "[Building and installing selinux policy for redmine_git_hosting:"
+ sh "#{Rails.root}/vendor/plugins/redmine_git_hosting/selinux/redmine_git.sh"
puts "DONE.]"
end
From 2f1c213c0ce41d024ad2b86585e4dea99c25b2c7 Mon Sep 17 00:00:00 2001
From: John Kubiatowicz
Date: Tue, 10 Jan 2012 23:15:43 -0800
Subject: [PATCH 07/11] Continuing rewrite of redmine_git_hosting to
rationalize path handling.
It is important to remember to migrate_plugin settings, since there
are new settings and one setting has changed a bit (i.e. httpServer
should nolonger include a rails_root path). Thus, make sure to:
rake db:migrate_plugins
This commit includes a number of changes. First and foremost, the
update_repositories() function now handles deletes and repository
moves as well as all other changes. Consequently, :RESYNC_ALL will be
able to recover from a variety of problems involving movement of
repositories resulting from project parent changes. Note that changes
in the parent for a subtree of projects works much better than before.
Second, this commit adds new settings which allow (1) redmine-managed
repositories to be focused in a specific subdirectory of the gitolite
repository, (2) allow repositories to be named either hierarchically
(default) or concentrated in a single directory (flat) independent of
project parents, and (3) an extra path parameter which can be added to
the http URL for smart http access of repositories.
Third, added the "access" box at the top of the settings page which
shows explicitly how settings affect access parameters.
Random improvements include (1) rewrite of routes for smart HTTP to
better handle changes in parentage (and avoid need to change routes as
project parents change); (2) changes in settings now immediately
trigger resulting changes in state (before, the settings cache got in
the way); (3) all of the path functions (repository positions, ssh
access, http access) have been concentrated into a small number of
functions at the top of git_hosting.rb, rather than spread throughout
the code; (4) continuing the changes started with the original
performance fixes, cleaned up observer behavior triggered by changes
in project membership and settings.
---
README.mkd | 64 ++-
app/controllers/git_http_controller.rb | 114 +++--
app/models/git_hosting_observer.rb | 73 ++-
app/models/git_hosting_settings_observer.rb | 76 ++-
app/views/projects/_git_urls.erb | 21 +-
app/views/repositories/_git_urls.erb | 57 ++-
app/views/settings/_display_access.html.erb | 19 +
.../settings/_redmine_git_hosting.html.erb | 74 +--
assets/javascripts/git_url_display.js | 6 +-
assets/stylesheets/application.css | 21 +-
config/locales/bg.yml | 21 +-
config/locales/bs.yml | 21 +-
config/locales/ca.yml | 21 +-
config/locales/cs.yml | 21 +-
config/locales/da.yml | 21 +-
config/locales/de.yml | 21 +-
config/locales/el.yml | 21 +-
config/locales/en.yml | 21 +-
config/locales/es.yml | 21 +-
config/locales/fi.yml | 21 +-
config/locales/fr.yml | 21 +-
config/locales/gl.yml | 21 +-
config/locales/he.yml | 21 +-
config/locales/hu.yml | 21 +-
config/locales/id.yml | 21 +-
config/locales/it.yml | 21 +-
config/locales/ja.yml | 21 +-
config/locales/ko.yml | 21 +-
config/locales/lt.yml | 21 +-
config/locales/nl.yml | 21 +-
config/locales/no.yml | 21 +-
config/locales/pl.yml | 21 +-
config/locales/pt-BR.yml | 23 +-
config/locales/pt.yml | 22 +-
config/locales/ro.yml | 21 +-
config/locales/ru.yml | 21 +-
config/locales/sk.yml | 21 +-
config/locales/sl.yml | 21 +-
config/locales/sr.yml | 21 +-
config/locales/sv.yml | 21 +-
config/locales/th.yml | 21 +-
config/locales/tr.yml | 21 +-
config/locales/uk.yml | 21 +-
config/locales/vi.yml | 21 +-
config/locales/zh-TW.yml | 21 +-
config/locales/zh.yml | 21 +-
config/routes.rb | 33 +-
...20111220055819_add_settings_to_plugin_2.rb | 51 ++
init.rb | 13 +-
lib/git_adapter_hooks.rb | 13 +-
lib/git_hosting.rb | 466 ++++++++++--------
.../patches/projects_controller_patch.rb | 75 +--
.../patches/repositories_controller_patch.rb | 11 +-
lib/gitolite_conf.rb | 25 +
lib/gitolite_recycle.rb | 34 +-
55 files changed, 1461 insertions(+), 544 deletions(-)
create mode 100644 app/views/settings/_display_access.html.erb
create mode 100644 db/migrate/20111220055819_add_settings_to_plugin_2.rb
diff --git a/README.mkd b/README.mkd
index d9fe779be..69528e6c1 100755
--- a/README.mkd
+++ b/README.mkd
@@ -1,4 +1,4 @@
-# Redmine Git Hosting Plugin (v0.4.2x)
+# Redmine Git Hosting Plugin (v0.4.3x)
A ChiliProject / Redmine plugin which makes configuring your own git hosting easy. This plugin allows straightforward management
of gitolite and associated public keys, the git daemon, and integrates code from Scott Schacon's "grack" utility
@@ -99,40 +99,53 @@ root directory:
**(6)** It is best to set several plugin variables BEFORE you run the db:migrate\_plugins task in step 7. In particular it is important
-that the *httpServer*, *gitServer*, *gitUser*, *gitRepositoryBasePath*, *gitoliteIdentityFile* and *gitoliteIdentityPublicKeyFile*
-variables are set correctly. To adjust these variables, open an editor and edit [redmine_rails_root]/vendor/plugins/redmine_git_hosting/init.rb file.
-Starting on line 22 you will see the settings definitions you should edit.
+that the *httpServer*, *gitServer*, *gitUser*, *gitoliteIdentityFile* and *gitoliteIdentityPublicKeyFile*
+variables are set correctly. Others that should be set include *gitRepositoryBasePath*, *gitRedmineSubdir*, and *gitRepositoryHierarchy*; however,
+the default values for these variables should be sufficient for most installations.
+To adjust these variables, open an editor and edit [redmine_rails_root]/vendor/plugins/redmine_git_hosting/init.rb file.
+Starting on line 22, you will see the settings definitions you should edit.
-The *httpServer* variable should be set to the url used to access your Redmine site, e.g. www.my-own-personal-git-host-server.com. Note that if Redmine is not
-installed in the site root this should include the path to your Redmine root, e.g. www.my-own-personal-git-host-server.com/path/to/redmine
+The *httpServer* variable should be set to the hostname which will be used to access your Redmine site, e.g. www.my-own-personal-git-host-server.com. This variable
+may optionally include a port using the ':portnum' syntax, i.e. www.my-own-person-git-host-server.com:8000. Unlike earlier versions of this plugin, this variable should
+*not* include the path to your Redmine root.
+The *gitServer* variable should be set to the hostname which will be used to access the gitolite repositories via ssh. In most configurations, this
+variable will be identical to the *httpServer*, except for the fact that *gitServer* will never include an optional port number.
-The *gitServer* will usually be the same as the the httpServer variable -- this is the server name to use to access the gitolite repositories via ssh. This should be
-the hostname only, so this will be different from *httpServer* if Redmine is not installed in the site root. In other words, even if Redmine is installed in
-www.my-own-personal-git-host-server.com/path/to/redmine, *gitServer* should be set to www.my-own-personal-git-host-server.com
-
-
-The *gitUser* is the user under which gitolite is installed
+The *gitUser* is the user under which gitolite is installed.
+If you followed the above directions you will not need to modify the *gitoliteIdentityFile* or *gitoliteIdentityPublicKeyFile* variables -- these specify
+the path to the private/public key files for accessing the gitolite admin repository.
-The *gitRepositoryBasePath* is the path *relative to the git user root* where the repositories are located. This should always end in a file separator, e.g. '/'.
-Since gitolite always uses repositories/ as the default place for repositories you probably shouldn't have to change this.
+Although you can change the following three variables, their default values provide for a very reasonable installation:
+The *gitRepositoryBasePath* is the path *relative to the git user root* where the repositories are located. This should always be non-empty and should end
+in a file separator, e.g. '/'. Since gitolite always uses repositories/ as the default place for repositories you probably shouldn't have to change this.
-If you followed the above directions you will not need to modify the *gitoliteIdentityFile* or *gitoliteIdentityPublicKeyFile* variables -- these specify
-the path to the private/public key files for accessing the gitolite admin repository.
+The *gitRedmineSubdir* is an optional subdirectory under the *gitRepositoryBasePath* which can be used for all plugin-managed repositories. Its default value
+is the empty string (no special subdirectory). If you choose to set it, make sure that the resulting path ends in a file separator, e.g. '/'.
+The *gitRepositoryHierarchy* variable is a boolean value which denotes whether or not the plugin-managed repositories are placed into a hierarchy that
+mirrors the project hierarchy. Its value is either 'true' (default) or 'false'.
These variables can be modified at a later time in the Administration => Plugins => Redmine Git Hosting Plugin configuration page. However to ensure
that the database migration from your existing repositories goes smoothly it is best to modify these variables now.
+As an example of the significance of the previous three variables, suppose that project-3 is a child of project-2 which is a child of project-1.
+Assume *gitRepositoryBasePath* == "repository/" and *gitRedmineSubdir* == "projects". When *gitRepositoryHierachy* is 'true', project-3.git will be stored in
+repository/projects/project-1/project-2/project-3.git, which will further be reflected in the ssh access URL of repository/projects/project-1/project-2/project-3.git.
+In contrast, when *gitRepositoryHierarchy* is 'false', project-3.git will be stored directly under repository/projects -- regardless of the number and identity of
+any parents that it might have. Note that the top of the settings page (Administration => Plugins => Redmine Git Hosting Plugin configuration page) provides
+information about how your chosen configuration affects the storage locations and URLs for accessing projects.
**(7)** Run the rake db:migrate\_plugins task to update the database. You will need to do this once for every
rails environment you have configured (e.g. production, development, testing). For the production environment run:
RAILS_ENV=production rake db:migrate_plugins
+At this point, if you wish to utilize selinux to protect your installation, you should follow the instructions given in the "Selinux" section, below.
+
**(8)** Unless you want to access your repositories exclusively via Smart HTTP users will need to set a
public key to connect via SSH. To do this, open a browser, login to ChiliProject/Redmine and follow the "My Account" Link
in the upper right-hand corner of the page. The right-hand column contains controls for adding your public key(s).
@@ -143,6 +156,9 @@ do not re-use the key you set as the gitolite admin key.
**(9)** The plugin is now configured, but you may now want to set some additional settings on the
Administration => Plugins => Redmine Git Hosting Plugin page.
+The *gitLockWaitTime* represents the amount of time that the plugin will wait in attempting to acquire its internal synchronization lock before giving up.
+You probably will not need to change this value.
+
*Automatically Initialize Git Repositories For New Projects* can be enabled to automatically create a new git repository every time
you create a new project. You won't have to create the project, and then create the repository -- this will be done all it one step. However, if
you have both git and svn (or hg, or cvs etc.) repositories, this may cause problems so it is disabled by default.
@@ -153,11 +169,8 @@ the accidental loss of data. If this feature is enabled, the safety is turned of
deleted in ChiliProject/Redmine. Note, however, that even when this feature is enabled, deleted repositories are placed into a "recycle_bin" for a configurable
amount of time (defaulting to 24 hours) and can be recovered by recreating the project in Redmine with the same Identifier. Details are placed in the log.
-The *gitRecycleBasePath* is the path *relative to the git user root* where deleted repositories are placed. This path should end in a path separator, e.g. '/'.
-Deleted repositories are kept here for up to *gitRecycleExpireTime* hours (configurable, defaults to 24.0 hours).
-
-The *gitLockWaitTime* represents the amount of time that the plugin will wait in attempting to acquire its internal synchronization lock before giving up.
-You probably will not need to change this value.
+The *Git Recycle Bin Base Path* is the path *relative to the git user root* where deleted repositories are placed. This path should end in a path separator,
+e.g. '/'. Deleted repositories are kept here for up to *gitRecycleExpireTime* hours (configurable, defaults to 24.0 hours).
*Show Checkout URLs* can be disabled to hide the git URL bar in the repository tab. It is enabled by default.
@@ -323,7 +336,14 @@ change options, then place your system back into enforcing mode. Alternatively,
file and reinstall the plugin. Under normal operation, you will get one selinux complaint about /bin/touch
in your log each time that you visit the plugin settings page.
-This rakefile and selinux configuration has been primarily tested on Redhat Enterprise Linux version 6.x
+One final comment: The selinux policy exists in binary form as selinux/redmine_git.pp. Should this policy
+need to be rebuilt, an additional rake task exists which will build the policy from selinux/redmine_git.te:
+
+ rake selinux:redmine_git_hosting:build_policy
+
+This task can be followed by the selinux:install task.
+
+The rakefile and selinux configuration has been primarily tested on Redhat Enterprise Linux version 6.x
with apache and fcgi. Other configurations may require slight tweaking.
## Tested Configurations
diff --git a/app/controllers/git_http_controller.rb b/app/controllers/git_http_controller.rb
index b5faaba1a..cd42f97a6 100644
--- a/app/controllers/git_http_controller.rb
+++ b/app/controllers/git_http_controller.rb
@@ -10,71 +10,83 @@ class GitHttpController < ApplicationController
before_filter :authenticate
-
def index
- p1 = params[:p1]
- p2 = params[:p2]
- p3 = params[:p3]
- proj_id = params[:id]
-
-
- @git_http_repo_path = (params[:path]).gsub(/\.git$/, "")
-
- reqfile = p2 == "" ? p1 : ( p3 == "" ? p1 + "/" + p2 : p1 + "/" + p2 + "/" + p3);
-
- if p1 == "git-upload-pack"
- service_rpc("upload-pack")
- elsif p1 == "git-receive-pack"
- service_rpc("receive-pack")
- elsif p1 == "info" && p2 == "refs"
- get_info_refs(reqfile)
- elsif p1 == "HEAD"
- get_text_file(reqfile)
- elsif p1 == "objects" && p2 == "info"
- if p3 != packs
- get_text_file(reqfile)
- else
- get_info_packs(reqfile)
- end
- elsif p1 == "objects" && p2 != "pack"
- get_loose_object(reqfile)
- elsif p1 == "objects" && p2 == "pack" && p3.match(/\.pack$/)
- get_pack_file(reqfile)
- elsif p1 == "objects" && p2 == "pack" && p3.match(/\.idx$/)
- get_idx_file(reqfile)
- else
- render_not_found
- end
-
+ if proj_path_split = (params[:project_path].match(/^(.*?([^\/]+))\.git$/))
+ proj_id = proj_path_split[2]
+ project = Project.find_by_identifier(proj_id)
+ @git_http_repo_path = project_path = proj_path_split[1]
+
+ if GitHosting.http_access_url(project) == project_path
+ p1 = params[:path][0] || ""
+ p2 = params[:path][1] || ""
+ p3 = params[:path][2] || ""
+
+ # Full requested path from .git repo
+ reqfile = params[:path].join('/')
+
+ # git http protocol
+ if p1 == "git-upload-pack"
+ service_rpc("upload-pack")
+ elsif p1 == "git-receive-pack"
+ service_rpc("receive-pack")
+ elsif p1 == "info" && p2 == "refs"
+ get_info_refs(reqfile)
+ elsif p1 == "HEAD"
+ get_text_file(reqfile)
+ elsif p1 == "objects" && p2 == "info"
+ if p3 != "packs"
+ get_text_file(reqfile)
+ else
+ get_info_packs(reqfile)
+ end
+ elsif p1 == "objects" && p2 != "pack"
+ get_loose_object(reqfile)
+ elsif p1 == "objects" && p2 == "pack" && p3.match(/\.pack$/)
+ get_pack_file(reqfile)
+ elsif p1 == "objects" && p2 == "pack" && p3.match(/\.idx$/)
+ get_idx_file(reqfile)
+ else
+ render_not_found
+ end
+ else
+ # repository URL doesn't match
+ render_not_found
+ end
+ return
+ end
+ # Wrong prefix or :base doesn't end in .git or doesn't at least have one char in base name
+ render_not_found
end
private
def authenticate
- is_push = params[:p1] == "git-receive-pack"
+ is_push = (params[:path][0] == "git-receive-pack")
query_valid = false
authentication_valid = true
- project = Project.find(params[:id])
- repository = project != nil ? project.repository : nil
- if(project != nil && repository !=nil)
- if repository.extra[:git_http] == 2 || (repository.extra[:git_http] == 1 && is_ssl?)
- query_valid = true
- allow_anonymous_read = project.is_public
- if is_push || (!allow_anonymous_read)
- authentication_valid = false
- authenticate_or_request_with_http_basic do |login, password|
- user = User.find_by_login(login);
- if user.is_a?(User)
- if user.allowed_to?( :commit_access, project ) || ((!is_push) && user.allowed_to?( :view_changesets, project ))
- authentication_valid = user.check_password?(password)
+ if proj_path_split = (params[:project_path].match(/^(.*?([^\/]+))\.git$/))
+ project = Project.find_by_identifier(proj_path_split[2])
+ repository = project != nil ? project.repository : nil
+ if(project != nil && repository !=nil)
+ if repository.extra[:git_http] == 2 || (repository.extra[:git_http] == 1 && is_ssl?)
+ query_valid = true
+ allow_anonymous_read = project.is_public
+ if is_push || (!allow_anonymous_read)
+ authentication_valid = false
+ authenticate_or_request_with_http_basic do |login, password|
+ user = User.find_by_login(login);
+ if user.is_a?(User)
+ if user.allowed_to?( :commit_access, project ) || ((!is_push) && user.allowed_to?( :view_changesets, project ))
+ authentication_valid = user.check_password?(password)
+ end
end
+ authentication_valid
end
- authentication_valid
end
end
end
- end
+ end
#if authentication failed, error already rendered
#so, just render case where user queried a project
diff --git a/app/models/git_hosting_observer.rb b/app/models/git_hosting_observer.rb
index 9599a0b7c..d254e8e1f 100644
--- a/app/models/git_hosting_observer.rb
+++ b/app/models/git_hosting_observer.rb
@@ -13,45 +13,40 @@ def reload_this_observer
end
- def self.set_update_active(is_active)
- case is_active
- when Symbol then @@updating_active_flags[is_active] = true
- when Hash then @updating_active_flags.merge(is_active)
- when Project then @@cached_project_updates << is_active
- end
-
- if !is_active
+ def self.set_update_active(*is_active)
+ if !is_active || !is_active.first
@@updating_active_stack += 1
else
- @@updating_active_stack -= 1
- if @@updating_active_stack < 0
- @@updating_active_stack = 0
- end
- end
-
- if is_active && @@updating_active_stack == 0
- if @@cached_project_updates.length > 0 || !@@updating_active_flags.empty?
+ is_active.each do |item|
+ case item
+ when Symbol then @@updating_active_flags[item] = true
+ when Hash then @@updating_active_flags.merge!(item)
+ when Project then @@cached_project_updates |= [item]
+ end
+ end
+
+ # If about to transition to zero and have something to run, do it
+ if @@updating_active_stack == 1 && (@@cached_project_updates.length > 0 || !@@updating_active_flags.empty?)
@@cached_project_updates = @@cached_project_updates.flatten.uniq.compact
GitHosting::update_repositories(@@cached_project_updates, @@updating_active_flags)
- @@cached_project_updates = []
- @@updating_active_flags = {}
- end
- @@updating_active = true
- else
- @@updating_active = false
- end
- end
+ @@cached_project_updates = []
+ @@updating_active_flags = {}
+ end
- def before_destroy(object)
- if object.is_a?(Repository::Git)
- if Setting.plugin_redmine_git_hosting['deleteGitRepositories'] == "true"
- GitHosting::delete_repository(object.project)
- %x[#{GitHosting::git_user_runner} 'rm -rf #{object.url}' ]
- end
- GitHosting::clear_cache_for_project(object.project)
+ # Wait until after running update_repositories before releasing
+ @@updating_active_stack -= 1
+ if @@updating_active_stack < 0
+ @@updating_active_stack = 0
+ end
end
+ @@updating_active = (@@updating_active_stack == 0)
end
-
+
+ # Register args for updating and then do it without allowing recursive calls
+ def self.bracketed_update_repositories(*args)
+ set_update_active(false)
+ set_update_active(*args)
+ end
def after_create(object)
if not object.is_a?(Project)
@@ -73,15 +68,18 @@ def after_save(object)
def after_destroy(object)
- if !object.is_a?(Repository::Git)
- update_repositories(object)
+ if object.is_a?(Repository::Git)
+ update_repositories(object,:delete=>true)
+ GitHosting::clear_cache_for_project(object.project)
+ else
+ update_repositories(object)
end
end
protected
- def update_repositories(object)
+ def update_repositories(object,*flags)
projects = []
case object
when Repository::Git then projects.push(object.project)
@@ -90,11 +88,12 @@ def update_repositories(object)
when Member then projects.push(object.project)
when Role then projects = object.members.map(&:project).flatten.uniq.compact
end
- if(projects.length > 0)
+ if (projects.length > 0)
if (@@updating_active)
- GitHosting::update_repositories(projects)
+ GitHosting::update_repositories(projects,*flags)
else
@@cached_project_updates.concat(projects)
+ @@updating_active_flags.merge!(*flags) unless flags.empty?
end
end
end
diff --git a/app/models/git_hosting_settings_observer.rb b/app/models/git_hosting_settings_observer.rb
index 884779cb6..e80e36156 100644
--- a/app/models/git_hosting_settings_observer.rb
+++ b/app/models/git_hosting_settings_observer.rb
@@ -25,7 +25,37 @@ def before_save(object)
valuehash['gitoliteIdentityFile'] = @@old_valuehash['gitoliteIdentityFile']
valuehash['gitoliteIdentityPublicKeyFile'] = @@old_valuehash['gitoliteIdentityPublicKeyFile']
end
+
+ # Server should not include any path components
+ if valuehash['gitServer']
+ normalizedServer = valuehash['gitServer'].lstrip.rstrip.split('/').first
+ if (normalizedServer == '')
+ valuehash['gitServer'] = @@old_valuehash['gitServer']
+ else
+ valuehash['gitServer'] = normalizedServer
+ end
+ end
+
+ # Server should not include any path components
+ if valuehash['httpServer']
+ normalizedServer = valuehash['httpServer'].lstrip.rstrip.split('/').first
+ if (normalizedServer == '')
+ valuehash['httpServer'] = @@old_valuehash['httpServer']
+ else
+ valuehash['httpServer'] = normalizedServer
+ end
+ end
+ # Normalize http repository subdirectory path, should be either empty or relative and end in '/'
+ if valuehash['httpServerSubdir']
+ normalizedFile = File.expand_path(valuehash['httpServerSubdir'].lstrip.rstrip,"/")
+ if (normalizedFile != "/")
+ valuehash['httpServerSubdir'] = normalizedFile[1..-1] + "/" # Clobber leading '/' add trailing '/'
+ else
+ valuehash['httpServerSubdir'] = ''
+ end
+ end
+
# Normalize Repository path, should be relative and end in '/'
if valuehash['gitRepositoryBasePath']
normalizedFile = File.expand_path(valuehash['gitRepositoryBasePath'].lstrip.rstrip,"/")
@@ -36,6 +66,16 @@ def before_save(object)
end
end
+ # Normalize Redmine Subdirectory path, should be either empty or relative and end in '/'
+ if valuehash['gitRedmineSubdir']
+ normalizedFile = File.expand_path(valuehash['gitRedmineSubdir'].lstrip.rstrip,"/")
+ if (normalizedFile != "/")
+ valuehash['gitRedmineSubdir'] = normalizedFile[1..-1] + "/" # Clobber leading '/' add trailing '/'
+ else
+ valuehash['gitRedmineSubdir'] = ''
+ end
+ end
+
# Normalize Recycle bin path, should be relative and end in '/'
if valuehash['gitRecycleBasePath']
normalizedFile = File.expand_path(valuehash['gitRecycleBasePath'].lstrip.rstrip,"/")
@@ -72,41 +112,43 @@ def after_save(object)
# Only perform after-actions on settings for our plugin
if object.name == "plugin_redmine_git_hosting"
valuehash = object.value
+
+ # Settings cache doesn't seem to invalidate symbolic versions of Settings immediately,
+ # so, any use of Setting.plugin_redmine_git_hosting[] by things called during this
+ # callback will be outdated.... True for at least some versions of redmine plugin...
+ #
+ # John Kubiatowicz 12/21/2011
+ if Setting.respond_to?(:check_cache)
+ # Clear out all cached settings.
+ Setting.check_cache
+ end
if GitHosting.bin_dir_writeable?
%x[ rm -rf '#{ GitHosting.get_tmp_dir }' ]
%x[ rm -rf '#{ GitHosting.get_bin_dir }' ]
end
- if @@old_valuehash['gitRepositoryBasePath'] != valuehash['gitRepositoryBasePath']
- GitHostingObserver.set_update_active(false)
- all_projects = Project.find(:all)
- all_projects.each do |p|
- if p.repository.is_a?(Repository::Git)
- r = p.repository
- repo_name= p.parent ? File.join(GitHosting::get_full_parent_path(p, true),p.identifier) : p.identifier
- r.url = File.join(valuehash['gitRepositoryBasePath'], "#{repo_name}.git")
- r.root_url = r.url
- r.save
- end
- end
- GitHostingObserver.set_update_active(true)
+ if @@old_valuehash['gitRepositoryBasePath'] != valuehash['gitRepositoryBasePath'] ||
+ @@old_valuehash['gitRedmineSubdir'] != valuehash['gitRedmineSubdir'] ||
+ @@old_valuehash['gitRepositoryHierarchy'] != valuehash['gitRepositoryHierarchy'] ||
+ @@old_valuehash['gitUser'] != valuehash['gitUser']
+ # Need to update everyone!
+ GitHostingObserver.bracketed_update_repositories(:resync_all)
end
if @@old_valuehash['gitUser'] != valuehash['gitUser']
GitHosting.setup_hooks
- GitHosting.update_repositories(:resync_all=>true)
elsif @@old_valuehash['httpServer'] != valuehash['httpServer'] ||
@@old_valuehash['gitHooksDebug'] != valuehash['gitHooksDebug'] ||
- @@old_valuehash['gitRepositoryBasePath'] != valuehash['gitRepositoryBasePath'] ||
+ @@old_valuehash['gitRedmineSubdir'] != valuehash['gitRedmineSubdir'] ||
+ @@old_valuehash['gitRepositoryHierarchy'] != valuehash['gitRepositoryHierarchy'] ||
@@old_valuehash['gitHooksAreAsynchronous'] != valuehash['gitHooksAreAsynchronous']
GitHosting.update_global_hook_params
end
-
- @@old_valuehash = (Setting.plugin_redmine_git_hosting).clone
+ @@old_valuehash = valuehash.clone
end
end
end
diff --git a/app/views/projects/_git_urls.erb b/app/views/projects/_git_urls.erb
index be2121e58..fa9c4388c 100644
--- a/app/views/projects/_git_urls.erb
+++ b/app/views/projects/_git_urls.erb
@@ -10,17 +10,22 @@