Añade plugin Redmine Git Hosting 4.0.2

This commit is contained in:
Manuel Cillero 2020-12-05 13:57:05 +01:00
parent 472cb1ea76
commit bdd66d941f
494 changed files with 36768 additions and 0 deletions

View file

@ -0,0 +1,13 @@
module Gitolitable
extend ActiveSupport::Concern
include Gitolitable::Authorizations
include Gitolitable::Cache
include Gitolitable::Config
include Gitolitable::Features
include Gitolitable::Notifications
include Gitolitable::Paths
include Gitolitable::Permissions
include Gitolitable::Urls
include Gitolitable::Users
include Gitolitable::Validations
end

View file

@ -0,0 +1,89 @@
module Gitolitable
module Authorizations
extend ActiveSupport::Concern
# These are for repository Gitolite configuration
def git_daemon_available?
User.anonymous.allowed_to?(:view_changesets, project) && git_daemon_enabled?
end
def git_web_available?
User.anonymous.allowed_to?(:browse_repository, project) && smart_http_enabled?
end
def protected_branches_available?
protected_branches_enabled? && project.active? && protected_branches.any?
end
def clonable_via_http?
User.anonymous.allowed_to?(:view_changesets, project) || smart_http_enabled?
end
def pushable_via_http?
https_access_enabled?
end
def git_notification_available?
git_notification_enabled? && !mailing_list.empty?
end
# These are for repository URLs
def urls_are_viewable?
RedmineGitHosting::Config.show_repositories_url? && User.current.allowed_to?(:view_changesets, project)
end
def ssh_access_available?
git_ssh_enabled? && !git_annex_enabled? && User.current.allowed_to_commit?(self)
end
def https_access_available?
https_access_enabled?
end
def http_access_available?
http_access_enabled?
end
def git_access_available?
(public_project? || public_repo?) && git_daemon_enabled?
end
def go_access_available?
(public_project? || public_repo?) && smart_http_enabled? && git_go_enabled?
end
def git_annex_access_available?
git_annex_enabled?
end
def downloadable?
git_annex_enabled? ? false : User.current.allowed_to_download?(self)
end
def deletable?
RedmineGitHosting::Config.delete_git_repositories?
end
def movable?
!identifier.nil? && !identifier.empty?
end
end
end

View file

@ -0,0 +1,90 @@
module Gitolitable
module Cache
extend ActiveSupport::Concern
included do
class << self
# Are repositories identifier unique?
#
def repo_ident_unique?
RedmineGitHosting::Config.unique_repo_identifier?
end
# Translate repository path into a unique ID for use in caching of git commands.
#
def repo_path_to_git_cache_id(repo_path)
repo = find_by_path(repo_path, loose: true)
repo ? repo.git_cache_id : nil
end
# Parse a path of the form <proj1>/<proj2>/<proj3>/<repo> and return the specified
# repository. If either 'repo_ident_unique?' is true or the <repo> is a project
# identifier, just return the last component. Otherwise,
# use the immediate parent (<proj3>) to try to identify the repo.
#
# Flags:
# :loose => true : Try to identify corresponding repo even if path is not quite correct
#
# Note that the :loose flag is used when interpreting the contents of the
# repository. If switching back and forth between the "repo_ident_unique?"
# form, it will still identify the repository (as long as there are not more than
# one repo with the same identifier.
#
# Example of data captured by regex :
# <MatchData "test/test2/test3/test4/test5.git" 1:"test4/" 2:"test4" 3:"test5" 4:".git">
# <MatchData "blabla2.git" 1:nil 2:nil 3:"blabla2" 4:".git">
#
def find_by_path(path, flags = {})
parseit = path.match(/\A.*?(([^\/]+)\/)?([^\/]+?)(\.git)?\z/)
return nil if parseit.nil?
project = Project.find_by_identifier(parseit[3])
# return default or first repo with blank identifier (or first Git repo--very rare?)
if project
project.repository || project.repo_blank_ident || project.gitolite_repos.first
elsif repo_ident_unique? || flags[:loose] && parseit[2].nil?
find_by_identifier(parseit[3])
elsif parseit[2]
project = Project.find_by_identifier(parseit[2])
if project.nil?
find_by_identifier(parseit[3])
else
find_by_identifier_and_project_id(parseit[3], project.id) || (flags[:loose] && find_by_identifier(parseit[3]))
end
end
end
end
end
# If repositories identifiers are unique, identifier forms a unique label,
# else use directory notation: <project identifier>/<repo identifier>
#
def git_cache_id
if identifier.blank?
# Should only happen with one repo/project (the default)
project.identifier
elsif self.class.repo_ident_unique?
identifier
else
"#{project.identifier}/#{identifier}"
end
end
# Note: RedmineGitHosting::Cache doesn't know about repository object, it only knows *git_cache_id*.
#
def empty_cache!
RedmineGitHosting::Cache.clear_cache_for_repository(git_cache_id)
end
end
end

View file

@ -0,0 +1,85 @@
module Gitolitable
module Config
extend ActiveSupport::Concern
def git_config
repo_conf = {}
# This is needed for all Redmine repositories
repo_conf['redminegitolite.projectid'] = project.identifier.to_s
repo_conf['redminegitolite.repositoryid'] = identifier || ''
repo_conf['redminegitolite.repositorykey'] = gitolite_hook_key
if project.active?
repo_conf['http.uploadpack'] = clonable_via_http?.to_s
repo_conf['http.receivepack'] = pushable_via_http?.to_s
if git_notification_available?
repo_conf['multimailhook.enabled'] = 'true'
repo_conf['multimailhook.mailinglist'] = mailing_list.join(', ')
repo_conf['multimailhook.from'] = sender_address
repo_conf['multimailhook.emailPrefix'] = email_prefix
else
repo_conf['multimailhook.enabled'] = 'false'
end
git_config_keys.each do |git|
repo_conf[git.key] = git.value
end if git_config_keys.any?
else
# Disable repository
repo_conf['http.uploadpack'] = 'false'
repo_conf['http.receivepack'] = 'false'
repo_conf['multimailhook.enabled'] = 'false'
end
repo_conf
end
def gitolite_options
repo_conf = {}
git_option_keys.each do |option|
repo_conf[option.key] = option.value
end if git_option_keys.any?
repo_conf
end
def owner
{ name: Setting['app_title'], email: Setting['mail_from'] }
end
def github_payload
{
repository: {
owner: owner,
description: project.description,
fork: false,
forks: 0,
homepage: project.homepage,
name: redmine_name,
open_issues: project.issues.open.length,
watchers: 0,
private: !project.is_public,
url: repository_url
},
pusher: owner,
}
end
def repository_url
Rails.application.routes.url_helpers.url_for(
controller: 'repositories', action: 'show',
id: project, repository_id: identifier_param,
only_path: false, host: Setting['host_name'], protocol: Setting['protocol']
)
end
end
end

View file

@ -0,0 +1,97 @@
module Gitolitable
module Features
extend ActiveSupport::Concern
# Always true to force repository fetch_changesets.
def report_last_commit
true
end
# Always true to force repository fetch_changesets.
def extra_report_last_commit
true
end
def git_default_branch
extra[:default_branch]
end
def gitolite_hook_key
extra[:key]
end
def git_daemon_enabled?
extra[:git_daemon]
end
def git_annex_enabled?
extra[:git_annex]
end
def git_notification_enabled?
extra[:git_notify]
end
def git_ssh_enabled?
extra[:git_ssh]
end
def git_go_enabled?
extra[:git_go]
end
def https_access_enabled?
extra[:git_https]
end
def http_access_enabled?
extra[:git_http]
end
def smart_http_enabled?
https_access_enabled? || http_access_enabled?
end
def only_https_access_enabled?
https_access_enabled? && !http_access_enabled?
end
def only_http_access_enabled?
http_access_enabled? && !https_access_enabled?
end
def protected_branches_enabled?
extra[:protected_branch]
end
def public_project?
project.is_public?
end
def public_repo?
extra[:public_repo]
end
def urls_order
extra[:urls_order]
end
end
end

View file

@ -0,0 +1,43 @@
module Gitolitable
module Notifications
extend ActiveSupport::Concern
def mailing_list
default_list + global_include_list - global_exclude_list
end
def default_list
watcher_users.map(&:email_address).map(&:address)
end
def global_include_list
RedmineGitHosting::Config.gitolite_notify_global_include
end
def global_exclude_list
RedmineGitHosting::Config.gitolite_notify_global_exclude
end
def sender_address
if extra.notification_sender.nil? || extra.notification_sender.empty?
RedmineGitHosting::Config.gitolite_notify_global_sender_address
else
extra.notification_sender
end
end
def email_prefix
if extra.notification_prefix.nil? || extra.notification_prefix.empty?
RedmineGitHosting::Config.gitolite_notify_global_prefix
else
extra.notification_prefix
end
end
end
end

View file

@ -0,0 +1,94 @@
module Gitolitable
module Paths
extend ActiveSupport::Concern
# This is the repository path from Redmine point of view.
# It is used to build HTTP(s) urls (including GoLang url).
# It doesn't contain references to internal directories like *gitolite_global_storage_dir* or *gitolite_redmine_storage_dir*
# to stay abstract from the real repository location.
# In this case, the real repository path is deduced from the path given thanks to the *find_by_path* method.
#
# Example : blabla/test-blabla/uuuuuuuuuuu/oooooo
#
# Call File.expand_path to add then remove heading /
#
def redmine_repository_path
File.expand_path(File.join('./', get_full_parent_path, git_cache_id), '/')[1..-1]
end
# This is the Gitolite repository identifier as it should appear in Gitolite config file.
# Example : redmine/blabla/test-blabla/uuuuuuuuuuu/oooooo
# (with 'redmine' a subdir of the Gitolite storage directory)
#
# Call File.expand_path to add then remove heading /
#
def gitolite_repository_name
File.expand_path(File.join('./', RedmineGitHosting::Config.gitolite_redmine_storage_dir, get_full_parent_path, git_cache_id), '/')[1..-1]
end
# The Gitolite repository identifier with the .git extension.
# Example : redmine/blabla/test-blabla/uuuuuuuuuuu/oooooo.git
#
def gitolite_repository_name_with_extension
"#{gitolite_repository_name}.git"
end
# This is the relative path to the Gitolite repository.
# Example : repositories/redmine/blabla/test-blabla/uuuuuuuuuuu/oooooo.git
# (with 'repositories' the Gitolite storage directory).
#
def gitolite_repository_path
File.join(RedmineGitHosting::Config.gitolite_global_storage_dir, gitolite_repository_name_with_extension)
end
# This is the full absolute path to the Gitolite repository.
# Example : /home/git/repositories/redmine/blabla/test-blabla/uuuuuuuuuuu/oooooo.git
#
def gitolite_full_repository_path
File.join(RedmineGitHosting::Config.gitolite_home_dir, gitolite_repository_path)
end
# A syntaxic sugar used to move repository from a location to an other
# Example : repositories/blabla/test-blabla/uuuuuuuuuuu/oooooo
#
def new_repository_name
gitolite_repository_name
end
# Used to move repository from a location to an other.
# At this point repository url still points to the old location but
# it contains the Gitolite storage directory in its path and the '.git' extension.
# Strip them to get the old repository name.
# Example :
# before : repositories/redmine/blabla/test-blabla/uuuuuuuuuuu/oooooo.git
# after : redmine/blabla/test-blabla/uuuuuuuuuuu/oooooo
#
def old_repository_name
url.gsub(RedmineGitHosting::Config.gitolite_global_storage_dir, '').gsub('.git', '')
end
private
def get_full_parent_path
return '' if !RedmineGitHosting::Config.hierarchical_organisation?
parent_parts = []
p = project
while p.parent
parent_id = p.parent.identifier.to_s
parent_parts.unshift(parent_id)
p = p.parent
end
parent_parts.join('/')
end
end
end

View file

@ -0,0 +1,66 @@
module Gitolitable
module Permissions
extend ActiveSupport::Concern
def build_gitolite_permissions(old_perms = {})
permissions_builder.build(self, gitolite_users, old_perms)
end
# We assume here that ':gitolite_config_file' is different than 'gitolite.conf'
# like 'redmine.conf' with 'include "redmine.conf"' in 'gitolite.conf'.
# This way, we know that all repos in this file are managed by Redmine so we
# don't need to backup users
#
def backup_gitolite_permissions(current_permissions)
if protected_branches_available? || RedmineGitHosting::Config.gitolite_identifier_prefix == ''
{}
else
extract_permissions(current_permissions)
end
end
private
def permissions_builder
if protected_branches_available?
PermissionsBuilder::ProtectedBranches
else
PermissionsBuilder::Standard
end
end
SKIP_USERS = ['gitweb', 'daemon', 'DUMMY_REDMINE_KEY', 'REDMINE_ARCHIVED_PROJECT', 'REDMINE_CLOSED_PROJECT']
def extract_permissions(current_permissions)
old_permissions = {}
current_permissions.each do |perm, branch_settings|
old_permissions[perm] = {}
branch_settings.each do |branch, user_list|
next if user_list.empty?
new_user_list = []
user_list.each do |user|
# ignore these users
next if SKIP_USERS.include?(user)
# backup users that are not Redmine users
new_user_list.push(user) if !user.include?(RedmineGitHosting::Config.gitolite_identifier_prefix)
end
old_permissions[perm][branch] = new_user_list if new_user_list.any?
end
end
old_permissions
end
end
end

View file

@ -0,0 +1,122 @@
module Gitolitable
module Urls
extend ActiveSupport::Concern
def http_user_login
User.current.anonymous? ? '' : "#{User.current.login}@"
end
def git_access_path
gitolite_repository_name_with_extension
end
def http_access_path
"#{RedmineGitHosting::Config.http_server_subdir}#{redmine_repository_path}.git"
end
def go_access_path
"go/#{redmine_repository_path}"
end
def ssh_url
"ssh://#{RedmineGitHosting::Config.gitolite_user}@#{RedmineGitHosting::Config.ssh_server_domain}/#{git_access_path}"
end
def git_url
"git://#{RedmineGitHosting::Config.ssh_server_domain}/#{git_access_path}"
end
def http_url
"http://#{http_user_login}#{RedmineGitHosting::Config.http_root_url}/#{http_access_path}"
end
def https_url
"https://#{http_user_login}#{RedmineGitHosting::Config.https_root_url}/#{http_access_path}"
end
def git_annex_url
"#{RedmineGitHosting::Config.gitolite_user}@#{RedmineGitHosting::Config.ssh_server_domain}:#{git_access_path}"
end
# This is the url used by Go to clone repository
#
def go_access_url
return '' if !smart_http_enabled?
return https_url if https_access_available?
return http_url if http_access_available?
end
# This is the url to add in Go files
#
def go_url
return '' if !smart_http_enabled?
return "#{RedmineGitHosting::Config.https_root_url}/#{go_access_path}" if https_access_available?
return "#{RedmineGitHosting::Config.http_root_url}/#{go_access_path}" if http_access_available?
end
def ssh_access
{ url: ssh_url, committer: User.current.allowed_to_commit?(self).to_s }
end
## Unsecure channels (clear password), commit is disabled
def http_access
{ url: http_url, committer: 'false' }
end
def https_access
{ url: https_url, committer: User.current.allowed_to_commit?(self).to_s }
end
def git_access
{ url: git_url, committer: 'false' }
end
def git_annex_access
{ url: git_annex_url, committer: User.current.allowed_to_commit?(self).to_s }
end
def go_access
{ url: go_url, committer: 'false' }
end
def available_urls
hash = {}
hash[:ssh] = ssh_access if ssh_access_available?
hash[:https] = https_access if https_access_available?
hash[:http] = http_access if http_access_available?
hash[:git] = git_access if git_access_available?
hash[:go] = go_access if go_access_available?
hash[:git_annex] = git_annex_access if git_annex_access_available?
hash
end
def available_urls_sorted
return available_urls if urls_order.nil? || urls_order.empty?
hash = {}
urls_order.each do |url|
next if !available_urls[url.to_sym]
hash[url.to_sym] = available_urls[url.to_sym]
end
hash
end
end
end

View file

@ -0,0 +1,109 @@
module Gitolitable
module Users
extend ActiveSupport::Concern
def gitolite_users
if project.active?
users_for_active_project
elsif project.archived?
users_for_archived_project
else
users_for_closed_project
end
end
def users_for_active_project
data = {}
data[:rewind_users] = rewind_users + rewind_deploy_users
data[:write_users] = write_users
data[:read_users] = read_users + read_deploy_users
data[:developer_team] = developer_team
data[:all_read] = all_users
# Add other users
data[:read_users] << 'DUMMY_REDMINE_KEY' if read_users.empty? && write_users.empty? && rewind_users.empty?
data[:read_users] << 'gitweb' if git_web_available?
data[:read_users] << 'daemon' if git_daemon_available?
# Return users
data
end
def users_for_archived_project
data = {}
data[:read_users] = ['REDMINE_ARCHIVED_PROJECT']
data
end
def users_for_closed_project
data = {}
data[:read_users] = all_users
data[:read_users] << 'REDMINE_CLOSED_PROJECT'
data
end
def users
project.users_available
end
def rewind_users
@rewind_users ||= users.select { |u| u.allowed_to?(:manage_repository, project) }.map { |u| u.gitolite_identifier }.sort
end
def write_users
@write_users ||= users.select { |u| u.allowed_to?(:commit_access, project) }.map { |u| u.gitolite_identifier }.sort - rewind_users
end
def read_users
@read_users ||= users.select { |u| u.allowed_to?(:view_changesets, project) }.map { |u| u.gitolite_identifier }.sort - rewind_users - write_users
end
def developer_team
@developer_team ||= (rewind_users + write_users).sort
end
def all_users
@all_users ||= (rewind_users + write_users + read_users).sort
end
def rewind_deploy_users
deploy_users_for_keys(rewind_deploy_keys)
end
def read_deploy_users
deploy_users_for_keys(read_deploy_keys)
end
def rewind_deploy_keys
deploy_keys_by_perm('RW+')
end
def read_deploy_keys
deploy_keys_by_perm('R')
end
def deploy_keys_by_perm(perm)
deployment_credentials.active.select { |cred| cred.perm == perm }
end
def deploy_users_for_keys(keys)
keys.map { |cred| cred.gitolite_public_key.owner }
end
end
end

View file

@ -0,0 +1,122 @@
module Gitolitable
module Validations
extend ActiveSupport::Concern
included do
# Set URL ourself as relative path.
#
before_validation :set_git_urls
# Make sure that identifier does not match Gitolite Admin repository
#
validates_exclusion_of :identifier, in: %w(gitolite-admin)
# Place additional constraints on repository identifiers
# because of multi repos
#
validate :additional_constraints_on_identifier
validate :identifier_dont_change
validate :default_repository_has_identifier
class << self
# Build a hash of repository identifier :
# <repo_1_identifier> => 1
# <repo_2_identifier> => 1
# etc...
# If the same repository identifier is found many times, increment the corresponding counter.
# Repository identifiers are unique if all values of the hash are 1.
#
def identifiers_to_hash
self.all.map(&:identifier).inject(Hash.new(0)) do |h, x|
h[x] += 1 unless x.blank?
h
end
end
def have_duplicated_identifier?
(identifiers_to_hash.values.max || 0) > 1
end
end
end
def exists_in_gitolite?
RedmineGitHosting::Commands.sudo_dir_exists?(gitolite_repository_path)
end
def empty_in_gitolite?
RedmineGitHosting::Commands.sudo_repository_empty?(gitolite_repository_path)
end
def git_objects_count
RedmineGitHosting::Commands.sudo_git_objects_count(File.join(gitolite_repository_path, 'objects'))
end
def empty?
extra_info.nil? || (!extra_info.has_key?('heads') && !extra_info.has_key?('branches'))
end
def data_for_destruction
{
repo_name: gitolite_repository_name,
repo_path: gitolite_full_repository_path,
delete_repository: deletable?,
git_cache_id: git_cache_id
}
end
private
# Set up git urls for new repositories
#
def set_git_urls
self.url = gitolite_repository_path if self.url.blank?
self.root_url = self.url if self.root_url.blank?
end
# Check several aspects of repository identifier (only for Redmine 1.4+)
# 1) cannot equal identifier of any project
# 2) if repo_ident_unique? make sure that repo identifier is globally unique
# 3) cannot make this repo the default if there will be some other repo with blank identifier
#
def additional_constraints_on_identifier
if !identifier.blank? && (new_record? || identifier_changed?)
errors.add(:identifier, :cannot_equal_project) if Project.find_by_identifier(identifier)
# See if a repo for another project has the same identifier (existing validations already check for current project)
errors.add(:identifier, :taken) if self.class.repo_ident_unique? && Repository.where("identifier = ? and project_id <> ?", identifier, project.id).any?
end
end
# Make sure identifier hasn't changed. Allow null and blank
# Note that simply using identifier_changed doesn't seem to work
# if the identifier was "NULL" but the new identifier is ""
#
def identifier_dont_change
return if new_record?
errors.add(:identifier, :cannot_change) if (identifier_was.blank? && !identifier.blank?) || (!identifier_was.blank? && identifier_changed?)
end
# Need to make sure that we don't take the default slot away from a sibling repo with blank identifier
#
def default_repository_has_identifier
if project && (is_default? || set_as_default?)
possibles = Repository.where("project_id = ? and (identifier = '' or identifier is null)", project.id)
errors.add(:base, :blank_default_exists) if possibles.any? && (new_record? || possibles.detect { |x| x.id != id })
end
end
end
end