Merge branch 'master' into project_users_api

This commit is contained in:
miks 2012-09-08 10:50:24 +03:00
commit 4226458faf
927 changed files with 1015 additions and 797 deletions

View file

@ -29,14 +29,24 @@ module Gitlab
# name (required) - name for new project
# code (optional) - code for new project, uses project name if not set
# path (optional) - path for new project, uses project name if not set
# description (optional) - short project description
# default_branch (optional) - 'master' by default
# issues_enabled (optional) - enabled by default
# wall_enabled (optional) - enabled by default
# merge_requests_enabled (optional) - enabled by default
# wiki_enabled (optional) - enabled by default
# Example Request
# POST /projects
post do
project = {}
project[:name] = params[:name]
project[:code] = params[:code] || project[:name]
project[:path] = params[:path] || project[:name]
@project = Project.create_by_user(project, current_user)
params[:code] ||= params[:name]
params[:path] ||= params[:name]
project_attrs = {}
params.each_pair do |k ,v|
if Project.attribute_names.include? k
project_attrs[k] = v
end
end
@project = Project.create_by_user(project_attrs, current_user)
if @project.saved?
present @project, with: Entities::Project
else

View file

@ -1,202 +1,43 @@
require 'gitolite'
require 'timeout'
require 'fileutils'
require_relative 'gitolite_config'
# TODO: refactor & cleanup
module Gitlab
class Gitolite
class AccessDenied < StandardError; end
class InvalidKey < StandardError; end
def config
Gitlab::GitoliteConfig.new
end
def set_key key_id, key_content, projects
configure do |c|
c.update_keys(key_id, key_content)
c.update_projects(projects)
config.apply do |config|
config.write_key(key_id, key_content)
config.update_projects(projects)
end
end
def remove_key key_id, projects
configure do |c|
c.delete_key(key_id)
c.update_projects(projects)
config.apply do |config|
config.rm_key(key_id)
config.update_projects(projects)
end
end
def update_repository project
configure do |c|
c.update_project(project.path, project)
end
config.update_project!(project.path, project)
end
alias_method :create_repository, :update_repository
def remove_repository project
configure do |c|
c.destroy_project(project)
end
config.destroy_project!(project)
end
def url_to_repo path
Gitlab.config.ssh_path + "#{path}.git"
end
def initialize
# create tmp dir
@local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
end
def enable_automerge
configure do |git|
git.admin_all_repo
end
config.admin_all_repo!(project)
end
protected
def destroy_project(project)
FileUtils.rm_rf(project.path_to_repo)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
conf.rm_repo(project.path)
ga_repo.save
end
#update or create
def update_keys(user, key)
File.open(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) }
end
def delete_key(user)
File.unlink(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"))
`cd #{File.join(@local_dir,'gitolite')} ; git rm keydir/#{user}.pub`
end
# update or create
def update_project(repo_name, project)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
ga_repo.save
end
# Updates many projects and uses project.path as the repo path
# An order of magnitude faster than update_project
def update_projects(projects)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
projects.each do |project|
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
end
ga_repo.save
end
def update_project_config(project, conf)
repo_name = project.path
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
name_readers = project.repository_readers
name_writers = project.repository_writers
name_masters = project.repository_masters
pr_br = project.protected_branches.map(&:name).join("$ ")
repo.clean_permissions
# Deny access to protected branches for writers
unless name_writers.blank? || pr_br.blank?
repo.add_permission("-", pr_br.strip + "$ ", name_writers)
end
# Add read permissions
repo.add_permission("R", "", name_readers) unless name_readers.blank?
# Add write permissions
repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
repo
end
def admin_all_repo
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
owner_name = ""
# Read gitolite-admin user
#
begin
repo = conf.get_repo("gitolite-admin")
owner_name = repo.permissions[0]["RW+"][""][0]
raise StandardError if owner_name.blank?
rescue => ex
puts "Can't determine gitolite-admin owner".red
raise StandardError
end
# @ALL repos premission for gitolite owner
repo_name = "@all"
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
repo.add_permission("RW+", "", owner_name)
conf.add_repo(repo, true)
ga_repo.save
end
private
def pull
# create tmp dir
@local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
Dir.mkdir @local_dir
`git clone #{Gitlab.config.gitolite_admin_uri} #{@local_dir}/gitolite`
end
def push
Dir.chdir(File.join(@local_dir, "gitolite"))
`git add -A`
`git commit -am "Gitlab"`
`git push`
Dir.chdir(Rails.root)
FileUtils.rm_rf(@local_dir)
end
def configure
Timeout::timeout(30) do
File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
begin
f.flock(File::LOCK_EX)
pull
yield(self)
push
ensure
f.flock(File::LOCK_UN)
end
end
end
rescue Exception => ex
if ex.message =~ /is not a valid SSH key string/
raise Gitolite::InvalidKey.new("ssh key is not valid")
else
Gitlab::Logger.error(ex.message)
raise Gitolite::AccessDenied.new("gitolite timeout")
end
end
alias_method :create_repository, :update_repository
end
end

View file

@ -0,0 +1,203 @@
require 'gitolite'
require 'timeout'
require 'fileutils'
module Gitlab
class GitoliteConfig
class PullError < StandardError; end
class PushError < StandardError; end
attr_reader :config_tmp_dir, :ga_repo, :conf
def config_tmp_dir
@config_tmp_dir ||= File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
end
def ga_repo
@ga_repo ||= ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
end
def apply
Timeout::timeout(30) do
File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
begin
# Set exclusive lock
# to prevent race condition
f.flock(File::LOCK_EX)
# Pull gitolite-admin repo
# in tmp dir before do any changes
pull(config_tmp_dir)
# Build ga_repo object and @conf
# to access gitolite-admin configuration
@conf = ga_repo.config
# Do any changes
# in gitolite-admin
# config here
yield(self)
# Save changes in
# gitolite-admin repo
# before pusht it
ga_repo.save
# Push gitolite-admin repo
# to apply all changes
push(config_tmp_dir)
# Remove tmp dir
# wiith gitolite-admin
FileUtils.rm_rf(config_tmp_dir)
ensure
# unlock so other task cann access
# gitolite configuration
f.flock(File::LOCK_UN)
end
end
end
rescue PullError => ex
Gitlab::Logger.error("Pull error -> " + ex.message)
raise Gitolite::AccessDenied, ex.message
rescue PushError => ex
Gitlab::Logger.error("Push error -> " + " " + ex.message)
raise Gitolite::AccessDenied, ex.message
rescue Exception => ex
Gitlab::Logger.error(ex.class.name + " " + ex.message)
raise Gitolite::AccessDenied.new("gitolite timeout")
end
def destroy_project(project)
FileUtils.rm_rf(project.path_to_repo)
conf.rm_repo(project.path)
end
def destroy_project!(project)
apply do |config|
config.destroy_project(project)
end
end
def write_key(id, key)
File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f|
f.write(key.gsub(/\n/,''))
end
end
def rm_key(user)
File.unlink(File.join(config_tmp_dir, 'gitolite/keydir',"#{user}.pub"))
`cd #{File.join(config_tmp_dir,'gitolite')} ; git rm keydir/#{user}.pub`
end
# update or create
def update_project(repo_name, project)
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
end
def update_project!(repo_name, project)
apply do |config|
config.update_project(repo_name, project)
end
end
# Updates many projects and uses project.path as the repo path
# An order of magnitude faster than update_project
def update_projects(projects)
projects.each do |project|
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
end
end
def update_project_config(project, conf)
repo_name = project.path
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
name_readers = project.repository_readers
name_writers = project.repository_writers
name_masters = project.repository_masters
pr_br = project.protected_branches.map(&:name).join("$ ")
repo.clean_permissions
# Deny access to protected branches for writers
unless name_writers.blank? || pr_br.blank?
repo.add_permission("-", pr_br.strip + "$ ", name_writers)
end
# Add read permissions
repo.add_permission("R", "", name_readers) unless name_readers.blank?
# Add write permissions
repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
repo
end
# Enable access to all repos for gitolite admin.
# We use it for accept merge request feature
def admin_all_repo
owner_name = ""
# Read gitolite-admin user
#
begin
repo = conf.get_repo("gitolite-admin")
owner_name = repo.permissions[0]["RW+"][""][0]
raise StandardError if owner_name.blank?
rescue => ex
puts "Can't determine gitolite-admin owner".red
raise StandardError
end
# @ALL repos premission for gitolite owner
repo_name = "@all"
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
repo.add_permission("RW+", "", owner_name)
conf.add_repo(repo, true)
end
def admin_all_repo!
apply { |config| config.admin_all_repo }
end
private
def pull tmp_dir
Dir.mkdir tmp_dir
`git clone #{Gitlab.config.gitolite_admin_uri} #{tmp_dir}/gitolite`
unless File.exists?(File.join(tmp_dir, 'gitolite', 'conf', 'gitolite.conf'))
raise PullError, "unable to clone gitolite-admin repo"
end
end
def push tmp_dir
Dir.chdir(File.join(tmp_dir, "gitolite"))
system('git add -A')
system('git commit -am "GitLab"')
if system('git push')
Dir.chdir(Rails.root)
else
raise PushError, "unable to push gitolite-admin repo"
end
end
end
end

View file

@ -1,7 +1,8 @@
module Gitlab
# Custom parser for Gitlab-flavored Markdown
# Custom parser for GitLab-flavored Markdown
#
# It replaces references in the text with links to the appropriate items in Gitlab.
# It replaces references in the text with links to the appropriate items in
# GitLab.
#
# Supported reference formats are:
# * @foo for team members
@ -10,19 +11,20 @@ module Gitlab
# * $123 for snippets
# * 123456 for commits
#
# It also parses Emoji codes to insert images. See
# http://www.emoji-cheat-sheet.com/ for a list of the supported icons.
#
# Examples
#
# >> m = Markdown.new(...)
#
# >> m.parse("Hey @david, can you fix this?")
# >> gfm("Hey @david, can you fix this?")
# => "Hey <a href="/gitlab/team_members/1">@david</a>, can you fix this?"
#
# >> m.parse("Commit 35d5f7c closes #1234")
# >> gfm("Commit 35d5f7c closes #1234")
# => "Commit <a href="/gitlab/commits/35d5f7c">35d5f7c</a> closes <a href="/gitlab/issues/1234">#1234</a>"
class Markdown
include Rails.application.routes.url_helpers
include ActionView::Helpers
#
# >> gfm(":trollface:")
# => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
module Markdown
REFERENCE_PATTERN = %r{
([^\w&;])? # Prefix (1)
( # Reference (2)
@ -33,15 +35,57 @@ module Gitlab
([^\w&;])? # Suffix (6)
}x.freeze
EMOJI_PATTERN = %r{(:(\S+):)}.freeze
attr_reader :html_options
def initialize(project, html_options = {})
@project = project
# Public: Parse the provided text with GitLab-Flavored Markdown
#
# text - the source text
# html_options - extra options for the reference links as given to link_to
#
# Note: reference links will only be generated if @project is set
def gfm(text, html_options = {})
return text if text.nil?
# prevents the string supplied through the _text_ argument to be altered
text = text.dup
@html_options = html_options
# Extract pre blocks so they are not altered
# from http://github.github.com/github-flavored-markdown/
extractions = {}
text.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
md5 = Digest::MD5.hexdigest(match)
extractions[md5] = match
"{gfm-extraction-#{md5}}"
end
# TODO: add popups with additional information
text = parse(text)
# Insert pre block extractions
text.gsub!(/\{gfm-extraction-(\h{32})\}/) do
extractions[$1]
end
sanitize text.html_safe, attributes: ActionView::Base.sanitized_allowed_attributes + %w(id class)
end
private
# Private: Parses text for references and emoji
#
# text - Text to parse
#
# Note: reference links will only be generated if @project is set
#
# Returns parsed text
def parse(text)
text.gsub(REFERENCE_PATTERN) do |match|
# parse reference links
text.gsub!(REFERENCE_PATTERN) do |match|
prefix = $1 || ''
reference = $2
identifier = $3 || $4 || $5
@ -52,10 +96,28 @@ module Gitlab
else
match
end
end if @project
# parse emoji
text.gsub!(EMOJI_PATTERN) do |match|
if valid_emoji?($2)
image_tag("emoji/#{$2}.png", size: "20x20", class: 'emoji', title: $1, alt: $1)
else
match
end
end
text
end
private
# Private: Checks if an emoji icon exists in the image asset directory
#
# emoji - Identifier of the emoji as a string (e.g., "+1", "heart")
#
# Returns boolean
def valid_emoji?(emoji)
File.exists?(Rails.root.join('app', 'assets', 'images', 'emoji', "#{emoji}.png"))
end
# Private: Dispatches to a dedicated processing method based on reference
#

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# This file was placed here by Gitlab. It makes sure that your pushed commits
# This file was placed here by GitLab. It makes sure that your pushed commits
# will be processed properly.
while read oldrev newrev ref

View file

@ -1,12 +1,10 @@
IMPORT_DIRECTORY = 'import_projects'
desc "Imports existing Git repos into new projects from the import_projects folder"
task :import_projects, [:email] => :environment do |t, args|
REPOSITORY_DIRECTORY = Gitlab.config.git_base_path
desc "Imports existing Git repos from a directory into new projects in git_base_path"
task :import_projects, [:directory,:email] => :environment do |t, args|
user_email = args.email
repos_to_import = Dir.glob("#{IMPORT_DIRECTORY}/*")
import_directory = args.directory
repos_to_import = Dir.glob("#{import_directory}/*")
git_base_path = Gitlab.config.git_base_path
puts "Found #{repos_to_import.length} repos to import"
imported_count = 0
@ -14,11 +12,9 @@ task :import_projects, [:email] => :environment do |t, args|
failed_count = 0
repos_to_import.each do |repo_path|
repo_name = File.basename repo_path
repo_full_path = File.join(Rails.root, repo_path)
puts " Processing #{repo_name}"
clone_path = "#{REPOSITORY_DIRECTORY}/#{repo_name}.git"
clone_path = "#{git_base_path}#{repo_name}.git"
if Dir.exists? clone_path
if Project.find_by_code(repo_name)
@ -30,7 +26,7 @@ task :import_projects, [:email] => :environment do |t, args|
end
else
# Clone the repo
unless clone_bare_repo_as_git(repo_full_path, clone_path)
unless clone_bare_repo_as_git(repo_path, clone_path)
failed_count += 1
next
end
@ -48,14 +44,17 @@ task :import_projects, [:email] => :environment do |t, args|
puts "Finished importing #{imported_count} projects (skipped #{skipped_count}, failed #{failed_count})."
end
# Clones a repo as bare git repo using the git user
# Clones a repo as bare git repo using the git_user
def clone_bare_repo_as_git(existing_path, new_path)
git_user = Gitlab.config.ssh_user
begin
sh "sudo -u git -i git clone --bare '#{existing_path}' #{new_path}"
sh "sudo -u #{git_user} -i git clone --bare '#{existing_path}' #{new_path}"
true
rescue
rescue Exception=> msg
puts " ERROR: Faild to clone #{existing_path} to #{new_path}"
false
puts " Make sure #{git_user} can reach #{existing_path}"
puts " Exception-MSG: #{msg}"
false
end
end

View file

@ -1,6 +1,6 @@
namespace :gitlab do
namespace :gitolite do
desc "GITLAB | Write GITLAB hook for gitolite"
desc "GITLAB | Write GitLab hook for gitolite"
task :write_hooks => :environment do
gitolite_hooks_path = File.join(Gitlab.config.git_hooks_path, "common")
gitlab_hooks_path = Rails.root.join("lib", "hooks")