Patch #2525 » 0004-Redmine-management-of-Git-repositories.patch
| app/controllers/my_controller.rb | ||
|---|---|---|
| 48 | 48 |
@blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT |
| 49 | 49 |
end |
| 50 | 50 | |
| 51 |
def remove_key |
|
| 52 |
if params[:id] |
|
| 53 |
AuthorizedKeysEntry.remove_entry_by_key(params[:id]) |
|
| 54 |
redirect_to :action => 'account' |
|
| 55 |
end |
|
| 56 |
end |
|
| 57 | ||
| 51 | 58 |
# Edit user's account |
| 52 | 59 |
def account |
| 53 | 60 |
@user = User.current |
| ... | ... | |
| 57 | 64 |
@user.mail_notification = (params[:notification_option] == 'all') |
| 58 | 65 |
@user.pref.attributes = params[:pref] |
| 59 | 66 |
@user.pref[:no_self_notified] = (params[:no_self_notified] == '1') |
| 67 |
if params[:new_key] |
|
| 68 |
begin |
|
| 69 |
key = AuthorizedKeysEntry.new(params[:new_key],@user.login) |
|
| 70 |
key.save |
|
| 71 |
rescue ArgumentError => e |
|
| 72 |
@user.errors.add('Key', e.message)
|
|
| 73 |
end |
|
| 74 |
end |
|
| 60 | 75 |
if @user.save |
| 61 | 76 |
@user.pref.save |
| 62 | 77 |
@user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : []) |
| app/helpers/repositories_helper.rb | ||
|---|---|---|
| 164 | 164 |
end |
| 165 | 165 | |
| 166 | 166 |
def git_field_tags(form, repository) |
| 167 |
content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
|
|
| 167 |
if Setting.serve_git_repositories? and (repository == nil or repository.url.blank?) |
|
| 168 |
content_tag('p', form.text_field(:url, :value => GitManager.repositories_root + '/' + repository.project.identifier + '.git'), :label => 'Path to .git directory', :size => 60, :required => true)
|
|
| 169 |
else |
|
| 170 |
content_tag('p', form.text_field(:url, :label => 'Path to .git directory', :size => 60, :required => true, :disabled => (repository && !repository.root_url.blank?)))
|
|
| 171 |
end |
|
| 168 | 172 |
end |
| 169 | 173 | |
| 170 | 174 |
def cvs_field_tags(form, repository) |
| app/models/authorized_keys_entry.rb | ||
|---|---|---|
| 1 |
require "strscan" |
|
| 2 | ||
| 3 |
class AuthorizedKeysEntry |
|
| 4 |
COMMENT=/^#/ |
|
| 5 |
BLANK=/^\s+$/ |
|
| 6 |
KEY_TYPES=['ssh-1','ssh-dss','ssh-rsa'] |
|
| 7 |
# For ssh1 keys bits and exponent parts go into @key(key content) variable |
|
| 8 |
PARSERS = {'ssh-1' => /^(?:(.+) )?(\d+ \d+ [^ ]+)(?: (.+))$/,
|
|
| 9 |
'ssh-dss' => /^(?:(.+) )?ssh-dss ([^ ]+)(?: (.+))$/, |
|
| 10 |
'ssh-rsa' => /^(?:(.+) )?ssh-rsa ([^ ]+)(?: (.+))$/} |
|
| 11 |
KEY_DISPLAYNAMES={'ssh-1' => '', 'ssh-dss' => 'ssh-dss ', 'ssh-rsa' => 'ssh-rsa ' }
|
|
| 12 | ||
| 13 |
KEY_FORMAT=/\A[\w\/\+\=]+\z/ |
|
| 14 |
IDENTIFIER_FORMAT=/\A[\w@\.\-]+\z/ |
|
| 15 |
@@authorized_keys_filename="~/.ssh/authorized_keys" |
|
| 16 | ||
| 17 |
attr_reader :identifier, :key, :key_type |
|
| 18 |
attr_accessor :options |
|
| 19 | ||
| 20 |
# Create object from string from authorized_keys |
|
| 21 |
def initialize(authorized_keys_string = nil,identifier = nil) |
|
| 22 |
@identifier = identifier if identifier |
|
| 23 |
@key = '' |
|
| 24 |
@options = [] |
|
| 25 |
create_from_authorized_keys(authorized_keys_string) if authorized_keys_string != nil |
|
| 26 |
end |
|
| 27 | ||
| 28 |
def self.authorized_keys_filename=(filename) |
|
| 29 |
@@authorized_keys_filename=filename |
|
| 30 |
end |
|
| 31 | ||
| 32 |
def self.authorized_keys_filename |
|
| 33 |
@@authorized_keys_filename |
|
| 34 |
end |
|
| 35 | ||
| 36 |
def save |
|
| 37 |
@options = GitManager::get_authorized_keys_options_for_login(identifier) |
|
| 38 |
AuthorizedKeysEntry.add_entry(self) |
|
| 39 |
end |
|
| 40 | ||
| 41 |
def self.find_by_identifier(identifier) |
|
| 42 |
result = Array.new |
|
| 43 |
read_entries.each do |entry| |
|
| 44 |
result << entry if entry.identifier == identifier |
|
| 45 |
end |
|
| 46 |
return result |
|
| 47 |
end |
|
| 48 | ||
| 49 |
def to_s |
|
| 50 |
s = "" |
|
| 51 |
s += @options.join(",") + " " unless @options.empty?
|
|
| 52 |
s += KEY_DISPLAYNAMES[@key_type] + @key |
|
| 53 |
s += " " + @identifier if @identifier |
|
| 54 |
return s |
|
| 55 |
end |
|
| 56 | ||
| 57 |
def self.remove_entry_by_key(key) |
|
| 58 |
entries = self.read_entries |
|
| 59 |
entries = entries.delete_if { |e| e.key[0,40] == key[0,40] }
|
|
| 60 |
self.save_entries(entries) |
|
| 61 |
end |
|
| 62 | ||
| 63 |
def self.add_entry(entry) |
|
| 64 |
entries = self.read_entries |
|
| 65 |
entries << entry |
|
| 66 |
self.save_entries(entries) |
|
| 67 |
end |
|
| 68 | ||
| 69 |
private |
|
| 70 | ||
| 71 |
def get_key_type(line) |
|
| 72 |
KEY_TYPES.each do |k| |
|
| 73 |
return k if PARSERS[k].match(line) |
|
| 74 |
end |
|
| 75 |
return nil |
|
| 76 |
end |
|
| 77 | ||
| 78 |
def create_from_authorized_keys(line) |
|
| 79 |
@key_type = get_key_type(line) |
|
| 80 |
raise ArgumentError, "Invalid format of authorized_keys entry " + line if @key_type == nil |
|
| 81 |
m = PARSERS[@key_type].match(line) |
|
| 82 |
@options, @key = m[1], m[2] |
|
| 83 |
@identifier = m[3] unless @identifier |
|
| 84 |
@options = AuthorizedKeysEntry.parse_options(@options) |
|
| 85 |
end |
|
| 86 | ||
| 87 |
def self.parse_options(options) |
|
| 88 |
result = [] |
|
| 89 |
return result if options == nil |
|
| 90 |
scanner = StringScanner.new(options) |
|
| 91 |
while !scanner.eos? |
|
| 92 |
scanner.skip(/[ \t]*/) |
|
| 93 |
# scan a long option |
|
| 94 |
if out = scanner.scan(/[-a-z0-9A-Z_]+=\".*?\"/) or out = scanner.scan(/[-a-z0-9A-Z_]+/) |
|
| 95 |
result << out |
|
| 96 |
else |
|
| 97 |
# found an unscannable token, let's abort |
|
| 98 |
break |
|
| 99 |
end |
|
| 100 |
# eat a comma |
|
| 101 |
scanner.skip(/[\t]*,[\t]*/) |
|
| 102 |
end |
|
| 103 |
return result |
|
| 104 |
end |
|
| 105 | ||
| 106 |
# Reads all entries from a file |
|
| 107 |
def self.read_entries |
|
| 108 |
entries = Array.new |
|
| 109 |
filename = File.expand_path(@@authorized_keys_filename) |
|
| 110 |
if !File.exists?(filename) |
|
| 111 |
return entries |
|
| 112 |
end |
|
| 113 | ||
| 114 |
File.open(filename) do |file| |
|
| 115 |
file.flock(File::LOCK_SH) |
|
| 116 |
begin |
|
| 117 |
file.each_line do |line| |
|
| 118 |
next if COMMENT.match(line) or BLANK.match(line) |
|
| 119 |
entries << AuthorizedKeysEntry.new(line) |
|
| 120 |
end |
|
| 121 |
ensure |
|
| 122 |
file.flock(File::LOCK_UN) |
|
| 123 |
end |
|
| 124 |
end |
|
| 125 |
entries |
|
| 126 |
end |
|
| 127 | ||
| 128 |
# Saves all entries to a file |
|
| 129 |
def self.save_entries(entries) |
|
| 130 |
filename = File.expand_path(@@authorized_keys_filename) |
|
| 131 |
FileUtils.mkdir_p(File.dirname(filename)) |
|
| 132 | ||
| 133 |
File.open(filename, "w") do |write_file| |
|
| 134 |
write_file.flock(File::LOCK_EX) |
|
| 135 |
entries.each do |entry| |
|
| 136 |
write_file << entry.to_s + "\n" |
|
| 137 |
end |
|
| 138 |
write_file.flock(File::LOCK_UN) |
|
| 139 |
end |
|
| 140 |
end |
|
| 141 |
end |
|
| app/models/changeset.rb | ||
|---|---|---|
| 70 | 70 |
scan_comment_for_issue_ids |
| 71 | 71 |
end |
| 72 | 72 |
require 'pp' |
| 73 |
|
|
| 74 |
def scan_comment_for_issue_ids |
|
| 75 |
return if comments.blank? |
|
| 73 | ||
| 74 |
# returns issue ids found in message |
|
| 75 |
# three arrays are returned, references issues, fixed ones and wrong numbers (not Issues) |
|
| 76 |
def self.find_issue_ids(message, project) |
|
| 77 |
return [[],[]] if message.blank? |
|
| 76 | 78 |
# keywords used to reference issues |
| 77 | 79 |
ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
|
| 78 | 80 |
# keywords used to fix issues |
| 79 | 81 |
fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
|
| 80 |
# status and optional done ratio applied |
|
| 81 |
fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id) |
|
| 82 |
done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i |
|
| 83 | 82 |
|
| 84 | 83 |
kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
|
| 85 |
return if kw_regexp.blank? |
|
| 84 |
return [[],[]] if kw_regexp.blank?
|
|
| 86 | 85 |
|
| 87 | 86 |
referenced_issues = [] |
| 87 |
fixed_issues = [] |
|
| 88 |
wrong_issue_ids = [] |
|
| 88 | 89 |
|
| 89 | 90 |
if ref_keywords.delete('*')
|
| 90 | 91 |
# find any issue ID in the comments |
| 91 | 92 |
target_issue_ids = [] |
| 92 |
comments.scan(%r{([\s\(,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
|
|
| 93 |
referenced_issues += repository.project.issues.find_all_by_id(target_issue_ids) |
|
| 93 |
message.scan(%r{([\s\(,-]|^)#(\d+)(?=[[:punct:]]|\s|<|$)}).each { |m| target_issue_ids << m[1] }
|
|
| 94 |
found_issues = project.issues.find_all_by_id(target_issue_ids) |
|
| 95 |
referenced_issues += found_issues |
|
| 96 |
found_issues.each { |issue| target_issue_ids.delete(issue.id.to_s) }
|
|
| 97 |
wrong_issue_ids += target_issue_ids |
|
| 94 | 98 |
end |
| 95 | 99 |
|
| 96 |
comments.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
|
|
| 100 |
message.scan(Regexp.new("(#{kw_regexp})[\s:]+(([\s,;&]*#?\\d+)+)", Regexp::IGNORECASE)).each do |match|
|
|
| 97 | 101 |
action = match[0] |
| 98 | 102 |
target_issue_ids = match[1].scan(/\d+/) |
| 99 |
target_issues = repository.project.issues.find_all_by_id(target_issue_ids) |
|
| 100 |
if fix_status && fix_keywords.include?(action.downcase) |
|
| 101 |
# update status of issues |
|
| 102 |
logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
|
|
| 103 |
target_issues.each do |issue| |
|
| 104 |
# the issue may have been updated by the closure of another one (eg. duplicate) |
|
| 105 |
issue.reload |
|
| 106 |
# don't change the status is the issue is closed |
|
| 107 |
next if issue.status.is_closed? |
|
| 108 |
csettext = "r#{self.revision}"
|
|
| 109 |
if self.scmid && (! (csettext =~ /^r[0-9]+$/)) |
|
| 110 |
csettext = "commit:\"#{self.scmid}\""
|
|
| 111 |
end |
|
| 112 |
journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext)) |
|
| 113 |
issue.status = fix_status |
|
| 114 |
issue.done_ratio = done_ratio if done_ratio |
|
| 115 |
issue.save |
|
| 116 |
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
|
|
| 117 |
end |
|
| 103 |
target_issues = project.issues.find_all_by_id(target_issue_ids) |
|
| 104 |
target_issues.each { |issue| target_issue_ids.delete(issue.id.to_s) }
|
|
| 105 |
wrong_issue_ids += target_issue_ids |
|
| 106 |
if fix_keywords.include?(action.downcase) |
|
| 107 |
fixed_issues += target_issues |
|
| 108 |
else |
|
| 109 |
referenced_issues += target_issues |
|
| 118 | 110 |
end |
| 119 |
referenced_issues += target_issues |
|
| 120 | 111 |
end |
| 121 |
|
|
| 122 |
self.issues = referenced_issues.uniq |
|
| 112 |
return [referenced_issues.uniq, fixed_issues.uniq, wrong_issue_ids.uniq] |
|
| 113 |
end |
|
| 114 |
|
|
| 115 |
def scan_comment_for_issue_ids |
|
| 116 |
return if comments.blank? |
|
| 117 |
# status and optional done ratio applied |
|
| 118 |
fix_status = IssueStatus.find_by_id(Setting.commit_fix_status_id) |
|
| 119 |
done_ratio = Setting.commit_fix_done_ratio.blank? ? nil : Setting.commit_fix_done_ratio.to_i |
|
| 120 | ||
| 121 |
referenced_issues, fixed_issues, wrong_issues = Changeset.find_issue_ids(comments, repository.project) |
|
| 122 | ||
| 123 |
# update status of issues |
|
| 124 |
logger.debug "Issues fixed by changeset #{self.revision}: #{issue_ids.join(', ')}." if logger && logger.debug?
|
|
| 125 |
fixed_issues.each do |issue| |
|
| 126 |
# the issue may have been updated by the closure of another one (eg. duplicate) |
|
| 127 |
issue.reload |
|
| 128 |
# don't change the status is the issue is closed |
|
| 129 |
next if issue.status.is_closed? |
|
| 130 |
csettext = "r#{self.revision}"
|
|
| 131 |
if self.scmid && (! (csettext =~ /^r[0-9]+$/)) |
|
| 132 |
csettext = "commit:\"#{self.scmid}\""
|
|
| 133 |
end |
|
| 134 |
journal = issue.init_journal(user || User.anonymous, l(:text_status_changed_by_changeset, csettext)) |
|
| 135 |
issue.status = fix_status |
|
| 136 |
issue.done_ratio = done_ratio if done_ratio |
|
| 137 |
issue.save |
|
| 138 |
Mailer.deliver_issue_edit(journal) if Setting.notified_events.include?('issue_updated')
|
|
| 139 |
end |
|
| 140 |
self.issues = (referenced_issues + fixed_issues).uniq |
|
| 123 | 141 |
end |
| 124 | 142 |
|
| 125 | 143 |
# Returns the previous changeset |
| app/models/git_checks/committer.rb | ||
|---|---|---|
| 1 |
class GitChecks::Committer < GitManager::CommitCheck |
|
| 2 |
def self.check(revision, branch, user, role, project, git) |
|
| 3 |
# Check: committer name and email |
|
| 4 |
if revision.author != "#{user.name} <#{user.mail}>"
|
|
| 5 |
return [ "Commit author name or email is wrong", |
|
| 6 |
" Execute following commands and _recreate_ commit:", |
|
| 7 |
" git config --global user.name \"#{user.name}\"",
|
|
| 8 |
" git config --global user.email #{user.mail}",
|
|
| 9 |
] |
|
| 10 |
end |
|
| 11 |
return [] |
|
| 12 |
end |
|
| 13 |
end |
|
| 14 | ||
| app/models/git_checks/delete_branch.rb | ||
|---|---|---|
| 1 |
class GitChecks::DeleteBranch < GitManager::RefCheck |
|
| 2 |
def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git) |
|
| 3 |
if new_rev_type == "delete" |
|
| 4 |
return "Deleting branch can be done only by repository manager" |
|
| 5 |
end |
|
| 6 |
return [] |
|
| 7 |
end |
|
| 8 |
end |
|
| 9 | ||
| app/models/git_checks/fast_forward.rb | ||
|---|---|---|
| 1 |
class GitChecks::FastForward < GitManager::RefCheck |
|
| 2 |
def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git) |
|
| 3 |
# Check: fast forward |
|
| 4 |
if old_rev != git.class::EMPTY_COMMIT and git.merge_base(old_rev, new_rev) != old_rev |
|
| 5 |
return "Only fast-forward commits are allowed" |
|
| 6 |
end |
|
| 7 |
return [] |
|
| 8 |
end |
|
| 9 |
end |
|
| 10 | ||
| app/models/git_checks/initial_commit.rb | ||
|---|---|---|
| 1 |
class GitChecks::InitialCommit < GitManager::RefCheck |
|
| 2 |
def self.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git) |
|
| 3 |
if old_rev == git.class::EMPTY_COMMIT and !role.allowed_to?(:manage_repository) |
|
| 4 |
return "Initial commit can be done only by repository manager" |
|
| 5 |
end |
|
| 6 |
return [] |
|
| 7 |
end |
|
| 8 |
end |
|
| 9 | ||
| app/models/git_checks/issue.rb | ||
|---|---|---|
| 1 |
class GitChecks::Issue < GitManager::CommitCheck |
|
| 2 |
def self.check(revision, branch, user, role, project, git) |
|
| 3 |
referenced_issues, fixes_issues, wrong_issues = Changeset.find_issue_ids(revision.message, project) |
|
| 4 |
issues = referenced_issues + fixes_issues |
|
| 5 | ||
| 6 |
errors = [] |
|
| 7 | ||
| 8 |
if !wrong_issues.empty? |
|
| 9 |
errors << "Some issue numbers are wrong: #{wrong_issues.join(',')}"
|
|
| 10 |
end |
|
| 11 | ||
| 12 |
issues.each do |issue_number| |
|
| 13 |
issue = Issue.find(issue_number) |
|
| 14 |
if issue.closed? |
|
| 15 |
errors << "Issue \##{issue.id} is closed. Reopen it and commit again"
|
|
| 16 |
end |
|
| 17 |
if issue.assigned_to != user |
|
| 18 |
if issue.assigned_to != nil |
|
| 19 |
owner_info = "It belongs to #{issue.assigned_to.firstname} #{issue.assigned_to.lastname}"
|
|
| 20 |
else |
|
| 21 |
owner_info = "It is unassigned" |
|
| 22 |
end |
|
| 23 |
errors << "Issue \##{issue.id} is not assigned to You. #{owner_info}"
|
|
| 24 |
end |
|
| 25 |
end |
|
| 26 |
return errors |
|
| 27 |
end |
|
| 28 |
end |
|
| 29 | ||
| app/models/git_manager.rb | ||
|---|---|---|
| 1 |
require 'net/http' |
|
| 2 |
require 'uri' |
|
| 3 |
require 'redmine/scm/adapters/git_adapter' |
|
| 4 | ||
| 5 |
class GitManager |
|
| 6 |
COMMANDS_READONLY = [ |
|
| 7 |
'git-upload-pack', |
|
| 8 |
] |
|
| 9 |
COMMANDS_WRITE = [ |
|
| 10 |
'git-receive-pack', |
|
| 11 |
] |
|
| 12 |
# TODO: make configurable |
|
| 13 |
REPOSITORIES_ROOT='~/repositories/' |
|
| 14 |
REDMINE_LOGIN_ENV_NAME='REDMINE_LOGIN' |
|
| 15 | ||
| 16 |
def self.repositories_root |
|
| 17 |
return File.expand_path(REPOSITORIES_ROOT) |
|
| 18 |
end |
|
| 19 | ||
| 20 |
|
|
| 21 |
# Executed by SSH when somebody logs into Redmine's SSH account |
|
| 22 |
# using public key. |
|
| 23 |
# Restricts the user to access only Git repositories he is allowed to. |
|
| 24 |
def self.serve |
|
| 25 |
begin |
|
| 26 |
command = serve_get_original_command |
|
| 27 |
user = serve_get_user |
|
| 28 |
git_command, project_identifier = parse_git_command(command) |
|
| 29 |
project, repository = find_project_and_repo(project_identifier) |
|
| 30 |
role = user.role_for_project(project) |
|
| 31 | ||
| 32 |
if COMMANDS_READONLY.include?(git_command) |
|
| 33 |
if !role.allowed_to?(:view_changesets) |
|
| 34 |
raise "User #{user} (#{role.name}) is not allowed to read project #{project}\n"
|
|
| 35 |
end |
|
| 36 |
if !repository.scm.info |
|
| 37 |
raise "Empty repository. Push some commit first" |
|
| 38 |
end |
|
| 39 |
elsif COMMANDS_WRITE.include?(git_command) |
|
| 40 |
if !role.allowed_to?(:commit_access) |
|
| 41 |
raise "User #{user} (#{role.name}) is not allowed to write to project #{project}\n"
|
|
| 42 |
end |
|
| 43 |
if !repository.scm.info |
|
| 44 |
warn "Creating new repository.\n" |
|
| 45 |
repository.scm.init(project.description) |
|
| 46 |
end |
|
| 47 |
else |
|
| 48 |
raise "Unknown command '#{verb}'"
|
|
| 49 |
end |
|
| 50 |
|
|
| 51 |
rescue |
|
| 52 |
warn "Error: #{$!}.\n"
|
|
| 53 |
exit 1 |
|
| 54 |
end |
|
| 55 | ||
| 56 |
ENV[REDMINE_LOGIN_ENV_NAME]=user.login |
|
| 57 |
exec 'git', 'shell', '-c', "#{git_command} '#{repository.url}'"
|
|
| 58 |
end |
|
| 59 | ||
| 60 |
def self.get_authorized_keys_options_for_login(login) |
|
| 61 |
return [ 'command="ruby ' + RAILS_ROOT + '/script/runner GitManager.serve \'' + login + '\' -e ' + ENV["RAILS_ENV"] + |
|
| 62 |
'"', 'no-port-forwarding','no-X11-forwarding','no-agent-forwarding','no-pty' ] |
|
| 63 |
end |
|
| 64 | ||
| 65 | ||
| 66 |
private |
|
| 67 |
def self.serve_get_original_command |
|
| 68 |
command = ENV["SSH_ORIGINAL_COMMAND"] |
|
| 69 |
if command == nil or command=="" |
|
| 70 |
raise "SSH_ORIGINAL_COMMAND not set. Use Git to access this account" |
|
| 71 |
end |
|
| 72 |
if command=~/\n/ |
|
| 73 |
raise "Command contains new line character" |
|
| 74 |
end |
|
| 75 |
return command |
|
| 76 |
end |
|
| 77 | ||
| 78 |
def self.serve_get_user |
|
| 79 |
login = ARGV[0] |
|
| 80 |
if login == nil or login=="" |
|
| 81 |
raise "Needs login as parameter" |
|
| 82 |
end |
|
| 83 |
user = User.find_by_login(login) |
|
| 84 |
if user == nil |
|
| 85 |
raise "User not found #{login}"
|
|
| 86 |
end |
|
| 87 |
return user |
|
| 88 |
end |
|
| 89 | ||
| 90 |
def self.parse_git_command(command) |
|
| 91 |
verb, args = command.split(" ",2)
|
|
| 92 |
if verb == 'git' |
|
| 93 |
subverb, args = args.split(" ",2)
|
|
| 94 |
verb = "%s-%s" % [ verb, subverb ] |
|
| 95 |
end |
|
| 96 |
project_identifier = args.gsub("'","")
|
|
| 97 |
return verb, project_identifier |
|
| 98 |
end |
|
| 99 | ||
| 100 |
def self.find_project_and_repo(project_identifier) |
|
| 101 |
project = Project.find_by_identifier(project_identifier) |
|
| 102 |
if project == nil |
|
| 103 |
raise "Project not found \"#{project_identifier}\""
|
|
| 104 |
end |
|
| 105 | ||
| 106 |
repository = project.repository |
|
| 107 |
if repository == nil |
|
| 108 |
raise "Project #{project} does not have repository\n"
|
|
| 109 |
end |
|
| 110 | ||
| 111 |
if repository.class != Repository::Git |
|
| 112 |
raise "Project #{project} has non git repository #{repository.type}"
|
|
| 113 |
end |
|
| 114 |
return project, repository |
|
| 115 |
end |
|
| 116 | ||
| 117 | ||
| 118 |
public |
|
| 119 | ||
| 120 |
class RefCheck |
|
| 121 |
@@checks = [] |
|
| 122 | ||
| 123 |
# Check Git ref, subclassess should override |
|
| 124 |
def check(old_rev, new_rev, new_rev_type, branch, user, role, project, git) |
|
| 125 |
return true |
|
| 126 |
end |
|
| 127 | ||
| 128 |
def self.inherited(subclass) |
|
| 129 |
@@checks << subclass |
|
| 130 |
end |
|
| 131 | ||
| 132 |
def self.get_checks |
|
| 133 |
return @@checks |
|
| 134 |
end |
|
| 135 |
end |
|
| 136 |
class CommitCheck |
|
| 137 |
@@checks = [] |
|
| 138 | ||
| 139 |
# Check Git ref, subclassess should override |
|
| 140 |
def check(revision, branch, user, role, project, git) |
|
| 141 |
return true |
|
| 142 |
end |
|
| 143 | ||
| 144 |
def self.inherited(subclass) |
|
| 145 |
@@checks << subclass |
|
| 146 |
end |
|
| 147 | ||
| 148 |
def self.get_checks |
|
| 149 |
return @@checks |
|
| 150 |
end |
|
| 151 |
end |
|
| 152 | ||
| 153 |
def self.load_checks |
|
| 154 |
files = Dir.glob(RAILS_ROOT + "/app/models/git_checks/*.rb") |
|
| 155 |
files.each do |f| |
|
| 156 |
f.sub!(/\A#{RAILS_ROOT}/,'')
|
|
| 157 |
f.split('/')[3..-1].join('/').split('.').first.camelize.constantize
|
|
| 158 |
end |
|
| 159 |
end |
|
| 160 |
load_checks |
|
| 161 | ||
| 162 |
def self.check_commits |
|
| 163 |
login = ENV[REDMINE_LOGIN_ENV_NAME] |
|
| 164 |
if login == nil |
|
| 165 |
warn "Redmine: Local user, not running checks" |
|
| 166 |
exit 0 |
|
| 167 |
end |
|
| 168 | ||
| 169 |
begin |
|
| 170 |
user = User.find_by_login(login) |
|
| 171 |
raise "User not found" if user == nil |
|
| 172 |
repository = Repository.find_by_url(Dir.getwd) |
|
| 173 |
raise "Repository not managed by Redmine" if repository == nil |
|
| 174 |
raise "Non git repository" if repository.class != Repository::Git |
|
| 175 |
project = repository.project |
|
| 176 |
role = user.role_for_project(project) |
|
| 177 |
git = repository.scm |
|
| 178 |
|
|
| 179 |
warn "" |
|
| 180 |
warn "-------------------------------------------------------------" |
|
| 181 |
warn "Redmine is checking your changes for correctness..." |
|
| 182 |
warn "Authenticated as #{user.name} (#{role.name} in #{project.name})"
|
|
| 183 | ||
| 184 |
error_found = false |
|
| 185 | ||
| 186 |
warn "Changes:" |
|
| 187 |
STDIN.each_line do |line| |
|
| 188 |
old_rev, new_rev, branch = line.split(" ")
|
|
| 189 |
|
|
| 190 |
if new_rev == git.class::EMPTY_COMMIT |
|
| 191 |
new_rev_type = "delete" |
|
| 192 |
else |
|
| 193 |
new_rev_type = git.get_object_type(new_rev) |
|
| 194 |
end |
|
| 195 |
revisions = nil |
|
| 196 |
if new_rev_type == "commit" |
|
| 197 |
revisions = git.revisions("", old_rev, new_rev)
|
|
| 198 |
end |
|
| 199 |
warn " Ref: #{branch} type: #{new_rev_type}"
|
|
| 200 | ||
| 201 |
errors = [] |
|
| 202 |
# TODO: make checks configurable |
|
| 203 |
RefCheck.get_checks.each do |check| |
|
| 204 |
result = check.check(old_rev, new_rev, new_rev_type, branch, user, role, project, git) |
|
| 205 |
if result.kind_of?(Array) |
|
| 206 |
errors += result |
|
| 207 |
else |
|
| 208 |
errors << result |
|
| 209 |
end |
|
| 210 |
end |
|
| 211 |
errors.each do |error| |
|
| 212 |
warn " Error: #{error}"
|
|
| 213 |
end |
|
| 214 |
error_found = true if !errors.empty? |
|
| 215 | ||
| 216 |
if revisions != nil |
|
| 217 |
revisions.each do |revision| |
|
| 218 |
warn " Commit: #{revision.identifier}"
|
|
| 219 | ||
| 220 |
errors = [] |
|
| 221 |
CommitCheck.get_checks.each do |check| |
|
| 222 |
result = check.check(revision, branch, user, role, project, git) |
|
| 223 |
if result.kind_of?(Array) |
|
| 224 |
errors += result |
|
| 225 |
else |
|
| 226 |
errors << result |
|
| 227 |
end |
|
| 228 |
end |
|
| 229 |
errors.each do |error| |
|
| 230 |
warn " Error: #{error}"
|
|
| 231 |
end |
|
| 232 |
error_found = true if !errors.empty? |
|
| 233 |
end |
|
| 234 |
end |
|
| 235 |
end |
|
| 236 |
end |
|
| 237 |
|
|
| 238 |
if error_found |
|
| 239 |
if !role.allowed_to?(:manage_repository) |
|
| 240 |
warn "Some commits were rejected. Correct them and try the push again." |
|
| 241 |
warn "-------------------------------------------------------------" |
|
| 242 |
warn "" |
|
| 243 |
exit 1 |
|
| 244 |
else |
|
| 245 |
warn "You are repository manager. Checks results are ignored. " |
|
| 246 |
warn "-------------------------------------------------------------" |
|
| 247 |
exit 0 |
|
| 248 |
end |
|
| 249 |
end |
|
| 250 |
warn "Changes look OK" |
|
| 251 |
warn "-------------------------------------------------------------" |
|
| 252 |
exit 0 |
|
| 253 |
end |
|
| 254 | ||
| 255 |
end |
|
| 256 | ||
| app/models/user.rb | ||
|---|---|---|
| 65 | 65 |
validates_length_of :password, :minimum => 4, :allow_nil => true |
| 66 | 66 |
validates_confirmation_of :password, :allow_nil => true |
| 67 | 67 | |
| 68 | ||
| 69 |
def ssh_key_entries |
|
| 70 |
AuthorizedKeysEntry.find_by_identifier(login) |
|
| 71 |
end |
|
| 72 | ||
| 68 | 73 |
def before_create |
| 69 | 74 |
self.mail_notification = false |
| 70 | 75 |
true |
| ... | ... | |
| 74 | 79 |
# update hashed_password if password was set |
| 75 | 80 |
self.hashed_password = User.hash_password(self.password) if self.password |
| 76 | 81 |
end |
| 82 | ||
| 77 | 83 |
|
| 78 | 84 |
def reload(*args) |
| 79 | 85 |
@name = nil |
| ... | ... | |
| 271 | 277 |
def self.hash_password(clear_password) |
| 272 | 278 |
Digest::SHA1.hexdigest(clear_password || "") |
| 273 | 279 |
end |
| 280 | ||
| 274 | 281 |
end |
| 275 | 282 | |
| 276 | 283 |
class AnonymousUser < User |
| app/views/my/account.rhtml | ||
|---|---|---|
| 15 | 15 |
<p><%= f.text_field :lastname, :required => true %></p> |
| 16 | 16 |
<p><%= f.text_field :mail, :required => true %></p> |
| 17 | 17 |
<p><%= f.select :language, lang_options_for_select %></p> |
| 18 |
<% if Setting.serve_git_repositories? %> |
|
| 19 |
<table> |
|
| 20 |
<% @user.ssh_key_entries.each do |e| %> |
|
| 21 |
<tr><td><%= e.identifier + " " + e.key[0,30] %>... </td><td><%= link_to 'Remove', {:action => 'remove_key', :id => e.key[0,40]}, :confirm => "Are you sure?" %></td></tr>
|
|
| 22 |
<% end %> |
|
| 23 |
</table> |
|
| 24 |
<p><%= text_area_tag :new_key, '' %></p> |
|
| 25 |
<% end %> |
|
| 26 | ||
| 18 | 27 |
</div> |
| 19 | 28 | |
| 20 | 29 |
<%= submit_tag l(:button_save) %> |
| app/views/settings/_repositories.rhtml | ||
|---|---|---|
| 14 | 14 |
<%= hidden_field_tag 'settings[enabled_scm][]', '' %> |
| 15 | 15 |
</p> |
| 16 | 16 | |
| 17 |
<% # TODO: Should be disabled when Git SCM is not enabled |
|
| 18 |
%> |
|
| 19 |
<p><label><%= l(:setting_serve_git_repositories) %></label> |
|
| 20 |
<%= check_box_tag 'settings[serve_git_repositories]', 1, Setting.serve_git_repositories? %> |
|
| 21 |
<%= hidden_field_tag 'settings[serve_git_repositories]', 0 %> |
|
| 22 |
</p> |
|
| 23 | ||
| 24 | ||
| 17 | 25 |
<p><label><%= l(:setting_repositories_encodings) %></label> |
| 18 | 26 |
<%= text_field_tag 'settings[repositories_encodings]', Setting.repositories_encodings, :size => 60 %><br /><em><%= l(:text_comma_separated) %></em></p> |
| 19 | 27 | |
| config/settings.yml | ||
|---|---|---|
| 77 | 77 |
default: 1 |
| 78 | 78 |
sys_api_enabled: |
| 79 | 79 |
default: 0 |
| 80 |
serve_git_repositories: |
|
| 81 |
default: 0 |
|
| 80 | 82 |
commit_ref_keywords: |
| 81 | 83 |
default: 'refs,references,IssueID' |
| 82 | 84 |
commit_fix_keywords: |
| lang/bg.yml | ||
|---|---|---|
| 92 | 92 |
field_firstname: Име |
| 93 | 93 |
field_lastname: Фамилия |
| 94 | 94 |
field_mail: Email |
| 95 |
field_ssh_key: SSH Public Key |
|
| 96 |
field_ssh_key_type: SSH Public Key Type |
|
| 95 | 97 |
field_filename: Файл |
| 96 | 98 |
field_filesize: Големина |
| 97 | 99 |
field_downloads: Downloads |
| ... | ... | |
| 181 | 183 |
setting_feeds_limit: Лимит на Feeds |
| 182 | 184 |
setting_autofetch_changesets: Автоматично обработване на ревизиите |
| 183 | 185 |
setting_sys_api_enabled: Разрешаване на WS за управление |
| 186 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 184 | 187 |
setting_commit_ref_keywords: Отбелязващи ключови думи |
| 185 | 188 |
setting_commit_fix_keywords: Приключващи ключови думи |
| 186 | 189 |
setting_autologin: Автоматичен вход |
| lang/ca.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Nom |
| 107 | 107 |
field_lastname: Cognom |
| 108 | 108 |
field_mail: Correu electrònic |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Fitxer |
| 110 | 112 |
field_filesize: Mida |
| 111 | 113 |
field_downloads: Baixades |
| ... | ... | |
| 202 | 204 |
setting_default_projects_public: Els projectes nous són públics per defecte |
| 203 | 205 |
setting_autofetch_changesets: Omple automàticament les publicacions |
| 204 | 206 |
setting_sys_api_enabled: Habilita el WS per a la gestió del dipòsit |
| 207 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 205 | 208 |
setting_commit_ref_keywords: Paraules claus per a la referència |
| 206 | 209 |
setting_commit_fix_keywords: Paraules claus per a la correcció |
| 207 | 210 |
setting_autologin: Entrada automàtica |
| lang/cs.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Jméno |
| 107 | 107 |
field_lastname: Příjmení |
| 108 | 108 |
field_mail: Email |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Soubor |
| 110 | 112 |
field_filesize: Velikost |
| 111 | 113 |
field_downloads: Staženo |
| ... | ... | |
| 201 | 203 |
setting_default_projects_public: Nové projekty nastavovat jako veřejné |
| 202 | 204 |
setting_autofetch_changesets: Autofetch commits |
| 203 | 205 |
setting_sys_api_enabled: Povolit WS pro správu repozitory |
| 206 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 204 | 207 |
setting_commit_ref_keywords: Klíčová slova pro odkazy |
| 205 | 208 |
setting_commit_fix_keywords: Klíčová slova pro uzavření |
| 206 | 209 |
setting_autologin: Automatické přihlašování |
| lang/da.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Fornavn |
| 107 | 107 |
field_lastname: Efternavn |
| 108 | 108 |
field_mail: E-mail |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Fil |
| 110 | 112 |
field_filesize: Størrelse |
| 111 | 113 |
field_downloads: Downloads |
| ... | ... | |
| 204 | 206 |
setting_sys_api_enabled: Aktiver webservice til versionsstyring |
| 205 | 207 |
setting_commit_ref_keywords: Nøgleord for sagsreferencer |
| 206 | 208 |
setting_commit_fix_keywords: Nøgleord for lukning af sager |
| 209 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 207 | 210 |
setting_autologin: Autologin |
| 208 | 211 |
setting_date_format: Datoformat |
| 209 | 212 |
setting_time_format: Tidsformat |
| lang/de.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Vorname |
| 107 | 107 |
field_lastname: Nachname |
| 108 | 108 |
field_mail: E-Mail |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Datei |
| 110 | 112 |
field_filesize: Größe |
| 111 | 113 |
field_downloads: Downloads |
| ... | ... | |
| 203 | 205 |
setting_default_projects_public: Neue Projekte sind standardmäßig öffentlich |
| 204 | 206 |
setting_autofetch_changesets: Changesets automatisch abrufen |
| 205 | 207 |
setting_sys_api_enabled: Webservice zur Verwaltung der Projektarchive benutzen |
| 208 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 206 | 209 |
setting_commit_ref_keywords: Schlüsselwörter (Beziehungen) |
| 207 | 210 |
setting_commit_fix_keywords: Schlüsselwörter (Status) |
| 208 | 211 |
setting_autologin: Automatische Anmeldung |
| lang/en.yml | ||
|---|---|---|
| 108 | 108 |
field_firstname: Firstname |
| 109 | 109 |
field_lastname: Lastname |
| 110 | 110 |
field_mail: Email |
| 111 |
field_new_key: SSH Public Key |
|
| 111 | 112 |
field_filename: File |
| 112 | 113 |
field_filesize: Size |
| 113 | 114 |
field_downloads: Downloads |
| ... | ... | |
| 205 | 206 |
setting_default_projects_public: New projects are public by default |
| 206 | 207 |
setting_autofetch_changesets: Autofetch commits |
| 207 | 208 |
setting_sys_api_enabled: Enable WS for repository management |
| 209 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 208 | 210 |
setting_commit_ref_keywords: Referencing keywords |
| 209 | 211 |
setting_commit_fix_keywords: Fixing keywords |
| 210 | 212 |
setting_autologin: Autologin |
| lang/es.yml | ||
|---|---|---|
| 150 | 150 |
field_lastname: Apellido |
| 151 | 151 |
field_login: Identificador |
| 152 | 152 |
field_mail: Correo electrónico |
| 153 |
field_ssh_key: SSH Public Key |
|
| 154 |
field_ssh_key_type: SSH Public Key Type |
|
| 153 | 155 |
field_mail_notification: Notificaciones por correo |
| 154 | 156 |
field_max_length: Longitud máxima |
| 155 | 157 |
field_min_length: Longitud mínima |
| ... | ... | |
| 627 | 629 |
setting_self_registration: Registro permitido |
| 628 | 630 |
setting_sequential_project_identifiers: Generar identificadores de proyecto |
| 629 | 631 |
setting_sys_api_enabled: Habilitar SW para la gestión del repositorio |
| 632 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 630 | 633 |
setting_text_formatting: Formato de texto |
| 631 | 634 |
setting_time_format: Formato de hora |
| 632 | 635 |
setting_user_format: Formato de nombre de usuario |
| lang/fi.yml | ||
|---|---|---|
| 101 | 101 |
field_firstname: Etunimi |
| 102 | 102 |
field_lastname: Sukunimi |
| 103 | 103 |
field_mail: Sähköposti |
| 104 |
field_ssh_key: SSH Public Key |
|
| 105 |
field_ssh_key_type: SSH Public Key Type |
|
| 104 | 106 |
field_filename: Tiedosto |
| 105 | 107 |
field_filesize: Koko |
| 106 | 108 |
field_downloads: Latausta |
| ... | ... | |
| 194 | 196 |
setting_feeds_limit: Syötteen sisällön raja |
| 195 | 197 |
setting_autofetch_changesets: Automaattisten muutosjoukkojen haku |
| 196 | 198 |
setting_sys_api_enabled: Salli WS tietovaraston hallintaan |
| 199 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 197 | 200 |
setting_commit_ref_keywords: Viittaavat hakusanat |
| 198 | 201 |
setting_commit_fix_keywords: Korjaavat hakusanat |
| 199 | 202 |
setting_autologin: Automaatinen kirjautuminen |
| lang/fr.yml | ||
|---|---|---|
| 108 | 108 |
field_firstname: Prénom |
| 109 | 109 |
field_lastname: Nom |
| 110 | 110 |
field_mail: Email |
| 111 |
field_ssh_key: SSH Public Key |
|
| 112 |
field_ssh_key_type: SSH Public Key Type |
|
| 111 | 113 |
field_filename: Fichier |
| 112 | 114 |
field_filesize: Taille |
| 113 | 115 |
field_downloads: Téléchargements |
| ... | ... | |
| 205 | 207 |
setting_default_projects_public: Définir les nouveaux projects comme publics par défaut |
| 206 | 208 |
setting_autofetch_changesets: Récupération auto. des commits |
| 207 | 209 |
setting_sys_api_enabled: Activer les WS pour la gestion des dépôts |
| 210 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 208 | 211 |
setting_commit_ref_keywords: Mot-clés de référencement |
| 209 | 212 |
setting_commit_fix_keywords: Mot-clés de résolution |
| 210 | 213 |
setting_autologin: Autologin |
| lang/he.yml | ||
|---|---|---|
| 94 | 94 |
field_firstname: שם פרטי |
| 95 | 95 |
field_lastname: שם משפחה |
| 96 | 96 |
field_mail: דוא"ל |
| 97 |
field_ssh_key: SSH Public Key |
|
| 98 |
field_ssh_key_type: SSH Public Key Type |
|
| 97 | 99 |
field_filename: קובץ |
| 98 | 100 |
field_filesize: גודל |
| 99 | 101 |
field_downloads: הורדות |
| ... | ... | |
| 184 | 186 |
setting_feeds_limit: גבול תוכן הזנות |
| 185 | 187 |
setting_autofetch_changesets: משיכה אוטומתי של עידכונים |
| 186 | 188 |
setting_sys_api_enabled: אפשר WS לניהול המאגר |
| 189 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 187 | 190 |
setting_commit_ref_keywords: מילות מפתח מקשרות |
| 188 | 191 |
setting_commit_fix_keywords: מילות מפתח מתקנות |
| 189 | 192 |
setting_autologin: חיבור אוטומטי |
| lang/hu.yml | ||
|---|---|---|
| 103 | 103 |
field_firstname: Keresztnév |
| 104 | 104 |
field_lastname: Vezetéknév |
| 105 | 105 |
field_mail: E-mail |
| 106 |
field_ssh_key: SSH Public Key |
|
| 107 |
field_ssh_key_type: SSH Public Key Type |
|
| 106 | 108 |
field_filename: Fájl |
| 107 | 109 |
field_filesize: Méret |
| 108 | 110 |
field_downloads: Letöltések |
| ... | ... | |
| 198 | 200 |
setting_default_projects_public: Az új projektek alapértelmezés szerint nyilvánosak |
| 199 | 201 |
setting_autofetch_changesets: Commitok automatikus lehúzása |
| 200 | 202 |
setting_sys_api_enabled: WS engedélyezése a tárolók kezeléséhez |
| 203 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 201 | 204 |
setting_commit_ref_keywords: Hivatkozó kulcsszavak |
| 202 | 205 |
setting_commit_fix_keywords: Javítások kulcsszavai |
| 203 | 206 |
setting_autologin: Automatikus bejelentkezés |
| lang/it.yml | ||
|---|---|---|
| 92 | 92 |
field_firstname: Nome |
| 93 | 93 |
field_lastname: Cognome |
| 94 | 94 |
field_mail: Email |
| 95 |
field_ssh_key: SSH Public Key |
|
| 96 |
field_ssh_key_type: SSH Public Key Type |
|
| 95 | 97 |
field_filename: File |
| 96 | 98 |
field_filesize: Dimensione |
| 97 | 99 |
field_downloads: Download |
| ... | ... | |
| 181 | 183 |
setting_feeds_limit: Limite contenuti del feed |
| 182 | 184 |
setting_autofetch_changesets: Acquisisci automaticamente le commit |
| 183 | 185 |
setting_sys_api_enabled: Abilita WS per la gestione del repository |
| 186 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 184 | 187 |
setting_commit_ref_keywords: Referencing keywords |
| 185 | 188 |
setting_commit_fix_keywords: Fixing keywords |
| 186 | 189 |
setting_autologin: Login automatico |
| lang/ja.yml | ||
|---|---|---|
| 93 | 93 |
field_firstname: 名前 |
| 94 | 94 |
field_lastname: 苗字 |
| 95 | 95 |
field_mail: メールアドレス |
| 96 |
field_ssh_key: SSH Public Key |
|
| 97 |
field_ssh_key_type: SSH Public Key Type |
|
| 96 | 98 |
field_filename: ファイル |
| 97 | 99 |
field_filesize: サイズ |
| 98 | 100 |
field_downloads: ダウンロード |
| ... | ... | |
| 182 | 184 |
setting_feeds_limit: フィード内容の上限 |
| 183 | 185 |
setting_autofetch_changesets: コミットを自動取得する |
| 184 | 186 |
setting_sys_api_enabled: リポジトリ管理用のWeb Serviceを有効にする |
| 187 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 185 | 188 |
setting_commit_ref_keywords: 参照用キーワード |
| 186 | 189 |
setting_commit_fix_keywords: 修正用キーワード |
| 187 | 190 |
setting_autologin: 自動ログイン |
| lang/ko.yml | ||
|---|---|---|
| 94 | 94 |
field_firstname: 이름 |
| 95 | 95 |
field_lastname: 성 |
| 96 | 96 |
field_mail: 메일 |
| 97 |
field_ssh_key: SSH Public Key |
|
| 98 |
field_ssh_key_type: SSH Public Key Type |
|
| 97 | 99 |
field_filename: 파일 |
| 98 | 100 |
field_filesize: 크기 |
| 99 | 101 |
field_downloads: 다운로드 |
| ... | ... | |
| 184 | 186 |
setting_feeds_limit: 내용 피드(RSS Feed) 제한 개수 |
| 185 | 187 |
setting_autofetch_changesets: 커밋된 변경묶음을 자동으로 가져오기 |
| 186 | 188 |
setting_sys_api_enabled: 저장소 관리자에 WS 를 허용 |
| 189 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 187 | 190 |
setting_commit_ref_keywords: 일감 참조에 사용할 키워드들 |
| 188 | 191 |
setting_commit_fix_keywords: 일감 해결에 사용할 키워드들 |
| 189 | 192 |
setting_autologin: 자동 로그인 |
| lang/lt.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Vardas |
| 107 | 107 |
field_lastname: Pavardė |
| 108 | 108 |
field_mail: Email |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Byla |
| 110 | 112 |
field_filesize: Dydis |
| 111 | 113 |
field_downloads: Atsiuntimai |
| ... | ... | |
| 203 | 205 |
setting_default_projects_public: Naujas projektas viešas pagal nutylėjimą |
| 204 | 206 |
setting_autofetch_changesets: Automatinis pakeitimų siuntimas |
| 205 | 207 |
setting_sys_api_enabled: Įgalinkite WS sandėlio vadybai |
| 208 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 206 | 209 |
setting_commit_ref_keywords: Nurodymo reikšminiai žodžiai |
| 207 | 210 |
setting_commit_fix_keywords: Fiksavimo reikšminiai žodžiai |
| 208 | 211 |
setting_autologin: Autoregistracija |
| lang/nl.yml | ||
|---|---|---|
| 148 | 148 |
field_onthefly: On-the-fly aanmaken van een gebruiker |
| 149 | 149 |
field_start_date: Start |
| 150 | 150 |
field_done_ratio: %% Gereed |
| 151 |
field_ssh_key: SSH Public Key |
|
| 152 |
field_ssh_key_type: SSH Public Key Type |
|
| 151 | 153 |
field_auth_source: Authenticatiemethode |
| 152 | 154 |
field_hide_mail: Verberg mijn e-mailadres |
| 153 | 155 |
field_comments: Commentaar |
| ... | ... | |
| 638 | 640 |
label_issue_watchers: Monitoren |
| 639 | 641 |
setting_commit_logs_encoding: Encodering van commit berichten |
| 640 | 642 |
button_quote: Citaat |
| 643 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 641 | 644 |
setting_sequential_project_identifiers: Genereer sequentiële projectidentiteiten |
| 642 | 645 |
notice_unable_delete_version: Niet mogelijk om deze versie te verwijderen. |
| 643 | 646 |
label_renamed: hernoemd |
| lang/no.yml | ||
|---|---|---|
| 105 | 105 |
field_firstname: Fornavn |
| 106 | 106 |
field_lastname: Etternavn |
| 107 | 107 |
field_mail: E-post |
| 108 |
field_ssh_key: SSH Public Key |
|
| 109 |
field_ssh_key_type: SSH Public Key Type |
|
| 108 | 110 |
field_filename: Fil |
| 109 | 111 |
field_filesize: Størrelse |
| 110 | 112 |
field_downloads: Nedlastinger |
| ... | ... | |
| 200 | 202 |
setting_default_projects_public: Nye prosjekter er offentlige som standard |
| 201 | 203 |
setting_autofetch_changesets: Autohenting av innsendinger |
| 202 | 204 |
setting_sys_api_enabled: Aktiver webservice for depot-administrasjon |
| 205 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 203 | 206 |
setting_commit_ref_keywords: Nøkkelord for referanse |
| 204 | 207 |
setting_commit_fix_keywords: Nøkkelord for retting |
| 205 | 208 |
setting_autologin: Autoinnlogging |
| lang/pl.yml | ||
|---|---|---|
| 161 | 161 |
field_lastname: Nazwisko |
| 162 | 162 |
field_login: Login |
| 163 | 163 |
field_mail: Email |
| 164 |
field_ssh_key: Klucz publiczny SSH |
|
| 165 |
field_ssh_key_type: Typ klucza SSH |
|
| 164 | 166 |
field_mail_notification: Powiadomienia Email |
| 165 | 167 |
field_max_length: Maksymalna długość |
| 166 | 168 |
field_min_length: Minimalna długość |
| ... | ... | |
| 657 | 659 |
setting_self_registration: Własna rejestracja umożliwiona |
| 658 | 660 |
setting_sequential_project_identifiers: Generuj sekwencyjne identyfikatory projektów |
| 659 | 661 |
setting_sys_api_enabled: Włączenie WS do zarządzania repozytorium |
| 662 |
setting_serve_git_repositories: Udostępnij repozytoria GIT poprzez konto SSH Redmine |
|
| 660 | 663 |
setting_text_formatting: Formatowanie tekstu |
| 661 | 664 |
setting_time_format: Format czasu |
| 662 | 665 |
setting_user_format: Personalny format wyświetlania |
| lang/pt-br.yml | ||
|---|---|---|
| 105 | 105 |
field_firstname: Nome |
| 106 | 106 |
field_lastname: Sobrenome |
| 107 | 107 |
field_mail: Email |
| 108 |
field_ssh_key: SSH Public Key |
|
| 109 |
field_ssh_key_type: SSH Public Key Type |
|
| 108 | 110 |
field_filename: Arquivo |
| 109 | 111 |
field_filesize: Tamanho |
| 110 | 112 |
field_downloads: Downloads |
| ... | ... | |
| 201 | 203 |
setting_default_projects_public: Novos projetos são públicos por padrão |
| 202 | 204 |
setting_autofetch_changesets: Auto-obter commits |
| 203 | 205 |
setting_sys_api_enabled: Ativa WS para gerenciamento do repositório |
| 206 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 204 | 207 |
setting_commit_ref_keywords: Palavras de referência |
| 205 | 208 |
setting_commit_fix_keywords: Palavras de fechamento |
| 206 | 209 |
setting_autologin: Auto-login |
| lang/pt.yml | ||
|---|---|---|
| 107 | 107 |
field_firstname: Nome |
| 108 | 108 |
field_lastname: Apelido |
| 109 | 109 |
field_mail: E-mail |
| 110 |
field_ssh_key: SSH Public Key |
|
| 111 |
field_ssh_key_type: SSH Public Key Type |
|
| 110 | 112 |
field_filename: Ficheiro |
| 111 | 113 |
field_filesize: Tamanho |
| 112 | 114 |
field_downloads: Downloads |
| ... | ... | |
| 203 | 205 |
setting_default_projects_public: Projectos novos são públicos por omissão |
| 204 | 206 |
setting_autofetch_changesets: Buscar automaticamente commits |
| 205 | 207 |
setting_sys_api_enabled: Activar Web Service para gestão do repositório |
| 208 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 206 | 209 |
setting_commit_ref_keywords: Palavras-chave de referência |
| 207 | 210 |
setting_commit_fix_keywords: Palavras-chave de fecho |
| 208 | 211 |
setting_autologin: Login automático |
| lang/ro.yml | ||
|---|---|---|
| 92 | 92 |
field_firstname: Nume |
| 93 | 93 |
field_lastname: Prenume |
| 94 | 94 |
field_mail: Email |
| 95 |
field_ssh_key: SSH Public Key |
|
| 96 |
field_ssh_key_type: SSH Public Key Type |
|
| 95 | 97 |
field_filename: Fisier |
| 96 | 98 |
field_filesize: Marimea fisierului |
| 97 | 99 |
field_downloads: Download |
| ... | ... | |
| 181 | 183 |
setting_feeds_limit: Limita continut feed |
| 182 | 184 |
setting_autofetch_changesets: Autofetch commits |
| 183 | 185 |
setting_sys_api_enabled: Setare WS pentru managementul stocului (repository) |
| 186 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 184 | 187 |
setting_commit_ref_keywords: Cuvinte cheie de referinta |
| 185 | 188 |
setting_commit_fix_keywords: Cuvinte cheie de rezolvare |
| 186 | 189 |
setting_autologin: Autentificare automata |
| lang/ru.yml | ||
|---|---|---|
| 165 | 165 |
field_lastname: Фамилия |
| 166 | 166 |
field_login: Пользователь |
| 167 | 167 |
field_mail: Email |
| 168 |
field_ssh_key: SSH Public Key |
|
| 169 |
field_ssh_key_type: SSH Public Key Type |
|
| 168 | 170 |
field_mail_notification: Уведомления по email |
| 169 | 171 |
field_max_length: Максимальная длина |
| 170 | 172 |
field_min_length: Минимальная длина |
| ... | ... | |
| 673 | 675 |
setting_self_registration: Возможна саморегистрация |
| 674 | 676 |
setting_sequential_project_identifiers: Генерировать последовательные идентификаторы проектов |
| 675 | 677 |
setting_sys_api_enabled: Разрешить WS для управления хранилищем |
| 678 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 676 | 679 |
setting_text_formatting: Форматирование текста |
| 677 | 680 |
setting_time_format: Формат времени |
| 678 | 681 |
setting_user_format: Формат отображения имени |
| lang/sk.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Meno |
| 107 | 107 |
field_lastname: Priezvisko |
| 108 | 108 |
field_mail: Email |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Súbor |
| 110 | 112 |
field_filesize: Veľkosť |
| 111 | 113 |
field_downloads: Stiahnuté |
| ... | ... | |
| 201 | 203 |
setting_default_projects_public: Nové projekty nastavovať ako verejné |
| 202 | 204 |
setting_autofetch_changesets: Automatický prenos zmien |
| 203 | 205 |
setting_sys_api_enabled: Povolit WS pre správu repozitory |
| 206 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 204 | 207 |
setting_commit_ref_keywords: Klúčové slová pre odkazy |
| 205 | 208 |
setting_commit_fix_keywords: Klúčové slová pre uzavretie |
| 206 | 209 |
setting_autologin: Automatické prihlasovanie |
| ... | ... | |
| 698 | 701 |
permission_edit_own_messages: Edit own messages |
| 699 | 702 |
permission_delete_own_messages: Delete own messages |
| 700 | 703 |
text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped." |
| 701 |
label_user_activity: "%s's activity" |
|
| 702 |
label_updated_time_by: Updated by %s %s ago |
|
| 703 |
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.' |
|
| 704 |
setting_diff_max_lines_displayed: Max number of diff lines displayed |
|
| 705 |
text_plugin_assets_writable: Plugin assets directory writable |
|
| 706 |
warning_attachments_not_saved: "%d file(s) could not be saved." |
|
| 707 |
button_create_and_continue: Create and continue |
|
| 704 |
label_user_activity: "%s's activity" |
|
| 705 |
label_updated_time_by: Updated by %s %s ago |
|
| 706 |
text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.' |
|
| 707 |
setting_diff_max_lines_displayed: Max number of diff lines displayed |
|
| 708 |
text_plugin_assets_writable: Plugin assets directory writable |
|
| 709 |
warning_attachments_not_saved: "%d file(s) could not be saved." |
|
| 710 |
button_create_and_continue: Create and continue |
|
| lang/sr.yml | ||
|---|---|---|
| 96 | 96 |
field_firstname: Ime |
| 97 | 97 |
field_lastname: Prezime |
| 98 | 98 |
field_mail: Email |
| 99 |
field_ssh_key: SSH Public Key |
|
| 100 |
field_ssh_key_type: SSH Public Key Type |
|
| 99 | 101 |
field_filename: Fajl |
| 100 | 102 |
field_filesize: Veličina |
| 101 | 103 |
field_downloads: Preuzimanja |
| ... | ... | |
| 186 | 188 |
setting_feeds_limit: Feed content limit |
| 187 | 189 |
setting_autofetch_changesets: Autofetch commits |
| 188 | 190 |
setting_sys_api_enabled: Ukljuci WS za menadžment spremišta |
| 191 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 189 | 192 |
setting_commit_ref_keywords: Referentne ključne reči |
| 190 | 193 |
setting_commit_fix_keywords: Fiksne ključne reči |
| 191 | 194 |
setting_autologin: Automatsko prijavljivanje |
| lang/sv.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Förnamn |
| 107 | 107 |
field_lastname: Efternamn |
| 108 | 108 |
field_mail: Mail |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Fil |
| 110 | 112 |
field_filesize: Storlek |
| 111 | 113 |
field_downloads: Nerladdningar |
| ... | ... | |
| 203 | 205 |
setting_default_projects_public: Nya projekt är publika som standard |
| 204 | 206 |
setting_autofetch_changesets: Automatisk hämtning av commits |
| 205 | 207 |
setting_sys_api_enabled: Aktivera WS för repository-hantering |
| 208 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 206 | 209 |
setting_commit_ref_keywords: Referens-nyckelord |
| 207 | 210 |
setting_commit_fix_keywords: Fix-nyckelord |
| 208 | 211 |
setting_autologin: Automatisk inloggning |
| lang/th.yml | ||
|---|---|---|
| 103 | 103 |
field_firstname: ชื่อ |
| 104 | 104 |
field_lastname: นามสกุล |
| 105 | 105 |
field_mail: อีเมล์ |
| 106 |
field_ssh_key: SSH Public Key |
|
| 107 |
field_ssh_key_type: SSH Public Key Type |
|
| 106 | 108 |
field_filename: แฟ้ม |
| 107 | 109 |
field_filesize: ขนาด |
| 108 | 110 |
field_downloads: ดาวน์โหลด |
| ... | ... | |
| 198 | 200 |
setting_default_projects_public: โครงการใหม่มีค่าเริ่มต้นเป็น สาธารณะ |
| 199 | 201 |
setting_autofetch_changesets: ดึง commits อัตโนมัติ |
| 200 | 202 |
setting_sys_api_enabled: เปิดใช้ WS สำหรับการจัดการที่เก็บต้นฉบับ |
| 203 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 201 | 204 |
setting_commit_ref_keywords: คำสำคัญ Referencing |
| 202 | 205 |
setting_commit_fix_keywords: คำสำคัญ Fixing |
| 203 | 206 |
setting_autologin: เข้าระบบอัตโนมัติ |
| lang/tr.yml | ||
|---|---|---|
| 102 | 102 |
field_firstname: Ad |
| 103 | 103 |
field_lastname: Soyad |
| 104 | 104 |
field_mail: E-Posta |
| 105 |
field_ssh_key: SSH Public Key |
|
| 106 |
field_ssh_key_type: SSH Public Key Type |
|
| 105 | 107 |
field_filename: Dosya |
| 106 | 108 |
field_filesize: Boyut |
| 107 | 109 |
field_downloads: İndirilenler |
| ... | ... | |
| 197 | 199 |
setting_default_projects_public: Yeni projeler varsayılan olarak herkese açık |
| 198 | 200 |
setting_autofetch_changesets: Otomatik gönderi al |
| 199 | 201 |
setting_sys_api_enabled: Depo yönetimi için WS'yi etkinleştir |
| 202 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 200 | 203 |
setting_commit_ref_keywords: Başvuru Kelimeleri |
| 201 | 204 |
setting_commit_fix_keywords: Sabitleme kelimeleri |
| 202 | 205 |
setting_autologin: Otomatik Giriş |
| lang/uk.yml | ||
|---|---|---|
| 97 | 97 |
field_firstname: Ім'я |
| 98 | 98 |
field_lastname: Прізвище |
| 99 | 99 |
field_mail: Ел. пошта |
| 100 |
field_ssh_key: SSH Public Key |
|
| 101 |
field_ssh_key_type: SSH Public Key Type |
|
| 100 | 102 |
field_filename: Файл |
| 101 | 103 |
field_filesize: Розмір |
| 102 | 104 |
field_downloads: Завантаження |
| ... | ... | |
| 189 | 191 |
setting_feeds_limit: Обмеження змісту подачі |
| 190 | 192 |
setting_autofetch_changesets: Автоматично доставати доповнення |
| 191 | 193 |
setting_sys_api_enabled: Дозволити WS для управління репозиторієм |
| 194 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 192 | 195 |
setting_commit_ref_keywords: Ключові слова для посилання |
| 193 | 196 |
setting_commit_fix_keywords: Призначення ключових слів |
| 194 | 197 |
setting_autologin: Автоматичний вхід |
| lang/vn.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: Tên lót + Tên |
| 107 | 107 |
field_lastname: Họ |
| 108 | 108 |
field_mail: Email |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: Tập tin |
| 110 | 112 |
field_filesize: Cỡ |
| 111 | 113 |
field_downloads: Tải về |
| ... | ... | |
| 202 | 204 |
setting_default_projects_public: Dự án mặc định là công cộng |
| 203 | 205 |
setting_autofetch_changesets: Autofetch commits |
| 204 | 206 |
setting_sys_api_enabled: Enable WS for repository management |
| 207 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 205 | 208 |
setting_commit_ref_keywords: Từ khóa tham khảo |
| 206 | 209 |
setting_commit_fix_keywords: Từ khóa chỉ vấn đề đã giải quyết |
| 207 | 210 |
setting_autologin: Tự động đăng nhập |
| lang/zh-tw.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: 名字 |
| 107 | 107 |
field_lastname: 姓氏 |
| 108 | 108 |
field_mail: 電子郵件 |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: 檔案名稱 |
| 110 | 112 |
field_filesize: 大小 |
| 111 | 113 |
field_downloads: 下載次數 |
| ... | ... | |
| 203 | 205 |
setting_autofetch_changesets: 自動取得送交版次 |
| 204 | 206 |
setting_default_projects_public: 新建立之專案預設為「公開」 |
| 205 | 207 |
setting_sys_api_enabled: 啟用管理版本庫之網頁服務 (Web Service) |
| 208 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 206 | 209 |
setting_commit_ref_keywords: 送交用於參照項目之關鍵字 |
| 207 | 210 |
setting_commit_fix_keywords: 送交用於修正項目之關鍵字 |
| 208 | 211 |
setting_autologin: 自動登入 |
| lang/zh.yml | ||
|---|---|---|
| 106 | 106 |
field_firstname: 名字 |
| 107 | 107 |
field_lastname: 姓氏 |
| 108 | 108 |
field_mail: 邮件地址 |
| 109 |
field_ssh_key: SSH Public Key |
|
| 110 |
field_ssh_key_type: SSH Public Key Type |
|
| 109 | 111 |
field_filename: 文件 |
| 110 | 112 |
field_filesize: 大小 |
| 111 | 113 |
field_downloads: 下载次数 |
| ... | ... | |
| 203 | 205 |
setting_default_projects_public: 新建项目默认为公开项目 |
| 204 | 206 |
setting_autofetch_changesets: 自动获取程序变更 |
| 205 | 207 |
setting_sys_api_enabled: 启用用于版本库管理的Web Service |
| 208 |
setting_serve_git_repositories: Serve GIT repositories using Redmine's SSH account |
|
| 206 | 209 |
setting_commit_ref_keywords: 用于引用问题的关键字 |
| 207 | 210 |
setting_commit_fix_keywords: 用于解决问题的关键字 |
| 208 | 211 |
setting_autologin: 自动登录 |
| lib/redmine/scm/adapters/git_adapter.rb | ||
|---|---|---|
| 24 | 24 |
|
- « Previous
- 1
- …
- 3
- 4
- 5
- Next »