Merge remote-tracking branch 'origin/sw_new_course' into sw_new_course
This commit is contained in:
commit
9a2e9a4ad2
1
Gemfile
1
Gemfile
|
@ -6,6 +6,7 @@ unless RUBY_PLATFORM =~ /w32/
|
|||
gem 'iconv'
|
||||
end
|
||||
|
||||
gem 'grack', path:'./lib/grack'
|
||||
gem 'rest-client'
|
||||
gem "mysql2", "= 0.3.18"
|
||||
gem 'redis-rails'
|
||||
|
|
|
@ -127,19 +127,18 @@ update
|
|||
end
|
||||
else # 原逻辑
|
||||
##xianbo
|
||||
params[:repository_scm] = "Git"
|
||||
@root_path=RepositoriesHelper::ROOT_PATH
|
||||
@repository_name=User.current.login.to_s+"/"+params[:repository][:identifier]+".git"
|
||||
@project_path=@root_path+"htdocs/"+@repository_name
|
||||
@repository_tag=params[:repository][:upassword] || params[:repository][:password]
|
||||
@repository_tag=params[:repository][:upassword] || params[:repository][:password] || '1234'
|
||||
@repo_name=User.current.login.to_s+"_"+params[:repository][:identifier]
|
||||
logger.info "htpasswd -mb "+@root_path+"htdocs/user.passwd "+@repo_name+": "+@repository_tag
|
||||
logger.info "the value of create repository"+@root_path+": "+@repository_name+": "+@project_path+": "+@repo_name
|
||||
attrs = pickup_extra_info
|
||||
if((@repository_tag!="")&¶ms[:repository_scm]=="Git")
|
||||
params[:repository][:url]=@project_path
|
||||
end
|
||||
params[:repository][:url]=@project_path
|
||||
###xianbo
|
||||
@repository = Repository.factory(params[:repository_scm])
|
||||
@repository = Repository.factory(params[:repository_scm]||"Git")
|
||||
@repository.safe_attributes = params[:repository]
|
||||
if attrs[:attrs_extra].keys.any?
|
||||
@repository.merge_extra_info(attrs[:attrs_extra])
|
||||
|
@ -278,7 +277,8 @@ update
|
|||
@course_tag = params[:course]
|
||||
project_path_cut = RepositoriesHelper::PROJECT_PATH_CUT
|
||||
ip = RepositoriesHelper::REPO_IP_ADDRESS
|
||||
@repos_url = "http://"+@repository.login.to_s+"_"+@repository.identifier.to_s+"@"+ip.to_s+
|
||||
# @repos_url = "http://"+@repository.login.to_s+"_"+@repository.identifier.to_s+"@"+ip.to_s+
|
||||
@repos_url = "http://#{Setting.host_name}/#{@repository.login.to_s}/#{@repository.identifier.to_s}.git"
|
||||
@repository.url.slice(project_path_cut, @repository.url.length).to_s
|
||||
if @course_tag == 1
|
||||
render :action => 'show', :layout => 'base_courses'
|
||||
|
|
|
@ -45,7 +45,7 @@ class UsersController < ApplicationController
|
|||
:activity_new_score_index, :influence_new_score_index, :score_new_index,:update_score,:user_activities,:user_projects_index,
|
||||
:user_courses4show,:user_projects4show,:user_course_activities,:user_project_activities,:user_feedback4show,:user_visitorlist,
|
||||
:user_resource,:user_resource_create,:user_resource_delete,:rename_resource,:search_user_course,:add_exist_file_to_course,
|
||||
:search_user_project,:resource_preview,:resource_search,:add_exist_file_to_project]
|
||||
:search_user_project,:resource_preview,:resource_search,:add_exist_file_to_project,:user_messages]
|
||||
#edit has been deleted by huang, 2013-9-23
|
||||
before_filter :find_user, :only => [:user_fanslist, :user_watchlist, :show, :edit, :update, :destroy, :edit_membership, :user_courses,
|
||||
:user_homeworks, :destroy_membership, :user_activities, :user_projects, :user_newfeedback, :user_comments,
|
||||
|
@ -177,7 +177,7 @@ class UsersController < ApplicationController
|
|||
return
|
||||
end
|
||||
respond_to do |format|
|
||||
format.html{render :layout=>'base_users_new'}
|
||||
format.html{render :layout=>'new_base_user'}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
module RepositoriesHelper
|
||||
if Rails.env.development?
|
||||
ROOT_PATH="/tmp/" if Rails.env.development?
|
||||
ROOT_PATH="/private/tmp/"
|
||||
else
|
||||
ROOT_PATH="/home/pdl/redmine-2.3.2-0/apache2/"
|
||||
end
|
||||
|
|
|
@ -38,7 +38,10 @@
|
|||
</ul>
|
||||
</div>
|
||||
<div class="navHomepageNews">
|
||||
<a href="javascript:void(0);" class="homepageNewsIcon">
|
||||
<%= link_to image_tag("/images/news_icon_small.png" , :width => "21", :height => "24"), {:controller=> 'users', :action => 'user_messages', id: User.current.id, host: Setting.host_user} %>
|
||||
<% if User.current.count_new_message >0 %>
|
||||
<div class="newsActive"></div>
|
||||
<% end %>
|
||||
<div class="newsActive"></div>
|
||||
</a></div>
|
||||
</div>
|
||||
</div>
|
|
@ -10,7 +10,7 @@
|
|||
<%= stylesheet_link_tag 'jquery/jquery-ui-1.9.2', 'new_public', 'user_leftside', :media => 'all' %>
|
||||
<%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
|
||||
<%= javascript_heads %>
|
||||
<%= javascript_include_tag "bootstrap","avatars","user"%>
|
||||
<%= javascript_include_tag "bootstrap","avatars","new_user"%>
|
||||
<%= heads_for_theme %>
|
||||
<%= call_hook :view_layouts_base_html_head %>
|
||||
<%= yield :header_tags -%>
|
||||
|
|
|
@ -62,15 +62,6 @@
|
|||
<%= labelled_form_for :repository, @repository, :url =>project_repositories_path(@project),:html => {:id => 'repository-form',:method=>"post"} do |f| %>
|
||||
<div id="pro_st_edit_ku" class="pro_st_edit_ku">
|
||||
<ul>
|
||||
<li >
|
||||
<label class="label02"><%=l(:label_scm)%>:</label>
|
||||
<%= select_tag('repository_scm',
|
||||
options_for_select(["Git"],@repository.class.name.demodulize),
|
||||
:data => {:remote => true, :method => 'get'})%>
|
||||
<% if @repository && ! @repository.class.scm_available %>
|
||||
<span class="c_grey"><%= l(:text_scm_command_not_available) %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
<% unless judge_main_repository(@project) %>
|
||||
<li>
|
||||
<label class="label02"><%=l(:field_repository_is_default)%>:</label>
|
||||
|
@ -84,14 +75,9 @@
|
|||
<span class="c_grey"><%=l(:text_length_between,:min=>1,:max=>254)<<l(:text_project_identifier_info) %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
<li >
|
||||
<label class="label02"><span class="c_red">*</span><%=l(:label_password)%></label>
|
||||
<%= f.password_field :upassword, :label=> "", :no_label => true %>
|
||||
<span class="c_grey"><%= l(:label_upassword_info)%></span>
|
||||
</li>
|
||||
<div class="cl"></div>
|
||||
</ul>
|
||||
<a href="#" onclick="$('#repository-form').submit();" class="blue_btn fl ml110"><%=l(:button_save)%></a>
|
||||
<a href="<%= settings_project_path(@project, :tab => 'repositories')%>" class="grey_btn fl ml10"><%=l(:button_cancel)%></a>
|
||||
</div><!--pro_st_edit_issues end-->
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -33,8 +33,9 @@
|
|||
<div id="repos_git_more">
|
||||
<br>
|
||||
<div class=" c_dark f14">
|
||||
<p color="red">git 克隆和提交的用户名和密码为登录用户名和密码 </p>
|
||||
<p>项目代码请设置好正确的编码方式(utf-8),否则中文会出现乱码。</p>
|
||||
<p>通过cmd命令提示符进入代码对应文件夹的根目录,假设当前用户的登录名为user,版本库名称为demo,需要操作的版本库分支为branch。
|
||||
<p>通过cmd命令提示符进入代码对应文件夹的根目录,
|
||||
如果是首次提交代码,执行如下命令:</p>
|
||||
</div>
|
||||
<div class="repos_explain">
|
||||
|
@ -45,19 +46,19 @@
|
|||
<p>git commit -m "first commit"</p>
|
||||
|
||||
<p>git remote add origin
|
||||
http://user_demo@repository.trustie.net/user/demo.git
|
||||
<%= @repos_url %>
|
||||
</p>
|
||||
|
||||
<p>git config http.postBuffer 524288000 #设置本地post缓存为500MB</p>
|
||||
|
||||
<p>git push -u origin branch:branch</p>
|
||||
<p>git push -u origin master</p>
|
||||
</div>
|
||||
<!--repos_explain end-->
|
||||
<div class="c_dark f14">
|
||||
<p>已经有本地库,还没有配置远程地址,打开命令行执行如下:</p>
|
||||
</div>
|
||||
<div class="repos_explain">
|
||||
<p>git remote add origin http://user_demo@repository.trustie.net/user/demo.git</p>
|
||||
<p>git remote add origin <%= @repos_url %></p>
|
||||
|
||||
<p>git add .</p>
|
||||
|
||||
|
@ -65,14 +66,14 @@
|
|||
|
||||
<p>git config http.postBuffer 524288000 #设置本地post缓存为500MB</p>
|
||||
|
||||
<p>git push -u origin branch:branch</p>
|
||||
<p>git push -u origin master</p>
|
||||
</div>
|
||||
<!--repos_explain end-->
|
||||
<div class="c_dark f14">
|
||||
<p>已有远程地址,创建一个远程分支,并切换到该分支,打开命令行执行如下:</p>
|
||||
</div>
|
||||
<div class="repos_explain">
|
||||
<p>git clone http://user_demo@repository.trustie.net/user/demo.git</p>
|
||||
<p>git clone <%= @repos_url %></p>
|
||||
|
||||
<p>git push</p>
|
||||
|
||||
|
@ -86,7 +87,7 @@
|
|||
</div>
|
||||
<div class="repos_explain">
|
||||
<p>git remote add trustie
|
||||
http://user_demo@repository.trustie.net/user/demo.git
|
||||
<%= @repos_url %>
|
||||
</p>
|
||||
|
||||
<p>git add .</p>
|
||||
|
|
|
@ -228,7 +228,7 @@
|
|||
<%# 用户留言消息 %>
|
||||
<% unless @user_feedback_messages.nil? %>
|
||||
<% @user_feedback_messages.each do |ufm| %>
|
||||
<% if ufm.journals_for_message_type == "Principal" %>
|
||||
<% if ufm.journals_for_message_type == "JournalsForMessage" %>
|
||||
<ul class="homepageNewsList fl">
|
||||
<li class="homepageNewsPortrait fl">
|
||||
<a href="javascript:void(0);"><%= image_tag(url_to_avatar(ufm.journals_for_message.user), :width => "30", :height => "30") %></a>
|
||||
|
|
|
@ -197,9 +197,12 @@ default:
|
|||
#max_concurrent_ajax_uploads: 2
|
||||
#pic_types: "bmp,jpeg,jpg,png,gif"
|
||||
|
||||
repository_root_path: '/tmp/htdocs'
|
||||
|
||||
# specific configuration options for production environment
|
||||
# that overrides the default ones
|
||||
production:
|
||||
repository_root_path: '/home/pdl/redmine-2.3.2-0/apache2/htdocs'
|
||||
cookie_domain: ".trustie.net"
|
||||
rmagick_font_path: /usr/share/fonts/ipa-mincho/ipam.ttf
|
||||
email_delivery:
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
RedmineApp::Application.routes.draw do
|
||||
mount Mobile::API => '/api'
|
||||
|
||||
# Enable Grack support
|
||||
mount Trustie::Grack.new, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
|
||||
|
||||
resources :homework_users
|
||||
resources :no_uses
|
||||
delete 'no_uses', :to => 'no_uses#delete'
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
language: ruby
|
||||
env:
|
||||
- TRAVIS=true
|
||||
branches:
|
||||
only:
|
||||
- 'master'
|
||||
rvm:
|
||||
- 1.9.3-p327
|
||||
- 2.0.0
|
||||
before_script:
|
||||
- "bundle install"
|
||||
- "git submodule init"
|
||||
- "git submodule update"
|
||||
script: "bundle exec rake"
|
|
@ -0,0 +1,16 @@
|
|||
2.0.2
|
||||
- Revert MR that broke smart HTTP clients.
|
||||
|
||||
2.0.1
|
||||
- Make sure child processes get reaped after popen, again.
|
||||
|
||||
2.0.0
|
||||
- Use safer shell commands and avoid Dir.chdir
|
||||
- Restrict the environment for shell commands
|
||||
- Make Grack::Server thread-safe (zimbatm)
|
||||
- Make sure child processes get reaped after popen
|
||||
- Verify requested path is actually a Git directory (Ryan Canty)
|
||||
|
||||
|
||||
1.1.0
|
||||
- Modifies service_rpc to use chunked transfer (https://github.com/gitlabhq/grack/pull/1)
|
|
@ -0,0 +1,10 @@
|
|||
source "http://ruby.taobao.org"
|
||||
|
||||
gemspec
|
||||
|
||||
group :development do
|
||||
gem 'byebug'
|
||||
gem 'rake'
|
||||
gem 'pry'
|
||||
gem 'rack-test'
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
gitlab-grack (2.0.2)
|
||||
rack (~> 1.5.1)
|
||||
|
||||
GEM
|
||||
remote: http://ruby.taobao.org/
|
||||
specs:
|
||||
byebug (4.0.5)
|
||||
columnize (= 0.9.0)
|
||||
coderay (1.1.0)
|
||||
columnize (0.9.0)
|
||||
metaclass (0.0.1)
|
||||
method_source (0.8.2)
|
||||
mocha (0.14.0)
|
||||
metaclass (~> 0.0.1)
|
||||
pry (0.10.1)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
slop (~> 3.4)
|
||||
rack (1.5.2)
|
||||
rack-test (0.6.2)
|
||||
rack (>= 1.0)
|
||||
rake (10.1.0)
|
||||
slop (3.6.0)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
byebug
|
||||
gitlab-grack!
|
||||
mocha (~> 0.11)
|
||||
pry
|
||||
rack-test
|
||||
rake
|
|
@ -0,0 +1,95 @@
|
|||
Grack - Ruby/Rack Git Smart-HTTP Server Handler
|
||||
===============================================
|
||||
|
||||
[![Build Status](https://travis-ci.org/gitlabhq/grack.png)](https://travis-ci.org/gitlabhq/grack)
|
||||
[![Code Climate](https://codeclimate.com/github/gitlabhq/grack.png)](https://codeclimate.com/github/gitlabhq/grack)
|
||||
|
||||
This project aims to replace the builtin git-http-backend CGI handler
|
||||
distributed with C Git with a Rack application. This reason for doing this
|
||||
is to allow far more webservers to be able to handle Git smart http requests.
|
||||
|
||||
The default git-http-backend only runs as a CGI script, and specifically is
|
||||
only targeted for Apache 2.x usage (it requires PATH_INFO to be set and
|
||||
specifically formatted). So, instead of trying to get it to work with
|
||||
other CGI capable webservers (Lighttpd, etc), we can get it running on nearly
|
||||
every major and minor webserver out there by making it Rack capable. Rack
|
||||
applications can run with the following handlers:
|
||||
|
||||
* CGI
|
||||
* FCGI
|
||||
* Mongrel (and EventedMongrel and SwiftipliedMongrel)
|
||||
* WEBrick
|
||||
* SCGI
|
||||
* LiteSpeed
|
||||
* Thin
|
||||
|
||||
These web servers include Rack handlers in their distributions:
|
||||
|
||||
* Ebb
|
||||
* Fuzed
|
||||
* Phusion Passenger (which is mod_rack for Apache and for nginx)
|
||||
* Unicorn
|
||||
* Puma
|
||||
|
||||
With [Warbler](http://caldersphere.rubyforge.org/warbler/classes/Warbler.html),
|
||||
and JRuby, we can also generate a WAR file that can be deployed in any Java
|
||||
web application server (Tomcat, Glassfish, Websphere, JBoss, etc).
|
||||
|
||||
Since the git-http-backend is really just a simple wrapper for the upload-pack
|
||||
and receive-pack processes with the '--stateless-rpc' option, it does not
|
||||
actually re-implement very much.
|
||||
|
||||
Dependencies
|
||||
========================
|
||||
* Ruby - http://www.ruby-lang.org
|
||||
* Rack - http://rack.rubyforge.org
|
||||
* A Rack-compatible web server
|
||||
* Git >= 1.7 (currently the 'pu' branch)
|
||||
* Mocha (only for running the tests)
|
||||
|
||||
Quick Start
|
||||
========================
|
||||
$ gem install rack
|
||||
$ (edit config.ru to set git project path)
|
||||
$ rackup --host 127.0.0.1 -p 8080 config.ru
|
||||
$ git clone http://127.0.0.1:8080/schacon/grit.git
|
||||
|
||||
Contributing
|
||||
========================
|
||||
If you would like to contribute to the Grack project, I prefer to get
|
||||
pull-requests via GitHub. You should include tests for whatever functionality
|
||||
you add. Just fork this project, push your changes to your fork and click
|
||||
the 'pull request' button. To run the tests, you first need to install the
|
||||
'mocha' mocking library and initialize the submodule.
|
||||
|
||||
$ sudo gem install mocha
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
|
||||
Then you should be able to run the tests with a 'rake' command. You can also
|
||||
run coverage tests with 'rake rcov' if you have rcov installed.
|
||||
|
||||
License
|
||||
========================
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009 Scott Chacon <schacon@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env rake
|
||||
require "bundler/gem_tasks"
|
||||
|
||||
task :default => :test
|
||||
|
||||
desc "Run the tests."
|
||||
task :test do
|
||||
Dir.glob("tests/*_test.rb").each do |f|
|
||||
system "ruby #{f}"
|
||||
end
|
||||
end
|
||||
|
||||
desc "Run test coverage."
|
||||
task :rcov do
|
||||
system "rcov tests/*_test.rb -i lib/git_http.rb -x rack -x Library -x tests"
|
||||
system "open coverage/index.html"
|
||||
end
|
||||
|
||||
namespace :grack do
|
||||
desc "Start Grack"
|
||||
task :start do
|
||||
system('./bin/testserver')
|
||||
end
|
||||
end
|
||||
|
||||
desc "Start everything."
|
||||
multitask :start => [ 'grack:start' ]
|
|
@ -0,0 +1,6 @@
|
|||
#! /bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
cd $(dirname "$0")/..
|
||||
exec /usr/bin/env bundle exec pry -Ilib -r grack
|
|
@ -0,0 +1,24 @@
|
|||
#! /usr/bin/env ruby
|
||||
libdir = File.absolute_path( File.join( File.dirname(__FILE__), '../lib' ) )
|
||||
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
||||
|
||||
Bundler.require(:default, :development)
|
||||
|
||||
|
||||
require 'grack'
|
||||
require 'rack'
|
||||
root = File.absolute_path( File.join( File.dirname(__FILE__), '../examples' ) )
|
||||
app = Grack::Server.new({
|
||||
project_root: root,
|
||||
upload_pack: true,
|
||||
receive_pack:true
|
||||
})
|
||||
|
||||
app1= Rack::Builder.new do
|
||||
use Grack::Auth do |username, password|
|
||||
[username, password] == ['123', '455']
|
||||
end
|
||||
run app
|
||||
end
|
||||
|
||||
Rack::Server.start app: app1, Port: 3001
|
|
@ -0,0 +1 @@
|
|||
ref: refs/heads/master
|
|
@ -0,0 +1,6 @@
|
|||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = true
|
||||
ignorecase = true
|
||||
precomposeunicode = true
|
|
@ -0,0 +1 @@
|
|||
Unnamed repository; edit this file 'description' to name the repository.
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message taken by
|
||||
# applypatch from an e-mail message.
|
||||
#
|
||||
# The hook should exit with non-zero status after issuing an
|
||||
# appropriate message if it wants to stop the commit. The hook is
|
||||
# allowed to edit the commit message file.
|
||||
#
|
||||
# To enable this hook, rename this file to "applypatch-msg".
|
||||
|
||||
. git-sh-setup
|
||||
test -x "$GIT_DIR/hooks/commit-msg" &&
|
||||
exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
|
||||
:
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to check the commit log message.
|
||||
# Called by "git commit" with one argument, the name of the file
|
||||
# that has the commit message. The hook should exit with non-zero
|
||||
# status after issuing an appropriate message if it wants to stop the
|
||||
# commit. The hook is allowed to edit the commit message file.
|
||||
#
|
||||
# To enable this hook, rename this file to "commit-msg".
|
||||
|
||||
# Uncomment the below to add a Signed-off-by line to the message.
|
||||
# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
|
||||
# hook is more suited to it.
|
||||
#
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
||||
|
||||
# This example catches duplicate Signed-off-by lines.
|
||||
|
||||
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||
echo >&2 Duplicate Signed-off-by lines.
|
||||
exit 1
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare a packed repository for use over
|
||||
# dumb transports.
|
||||
#
|
||||
# To enable this hook, rename this file to "post-update".
|
||||
|
||||
exec git update-server-info
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed
|
||||
# by applypatch from an e-mail message.
|
||||
#
|
||||
# The hook should exit with non-zero status after issuing an
|
||||
# appropriate message if it wants to stop the commit.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-applypatch".
|
||||
|
||||
. git-sh-setup
|
||||
test -x "$GIT_DIR/hooks/pre-commit" &&
|
||||
exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
|
||||
:
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to verify what is about to be committed.
|
||||
# Called by "git commit" with no arguments. The hook should
|
||||
# exit with non-zero status after issuing an appropriate message if
|
||||
# it wants to stop the commit.
|
||||
#
|
||||
# To enable this hook, rename this file to "pre-commit".
|
||||
|
||||
if git rev-parse --verify HEAD >/dev/null 2>&1
|
||||
then
|
||||
against=HEAD
|
||||
else
|
||||
# Initial commit: diff against an empty tree object
|
||||
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
|
||||
fi
|
||||
|
||||
# If you want to allow non-ASCII filenames set this variable to true.
|
||||
allownonascii=$(git config --bool hooks.allownonascii)
|
||||
|
||||
# Redirect output to stderr.
|
||||
exec 1>&2
|
||||
|
||||
# Cross platform projects tend to avoid non-ASCII filenames; prevent
|
||||
# them from being added to the repository. We exploit the fact that the
|
||||
# printable range starts at the space character and ends with tilde.
|
||||
if [ "$allownonascii" != "true" ] &&
|
||||
# Note that the use of brackets around a tr range is ok here, (it's
|
||||
# even required, for portability to Solaris 10's /usr/bin/tr), since
|
||||
# the square bracket bytes happen to fall in the designated range.
|
||||
test $(git diff --cached --name-only --diff-filter=A -z $against |
|
||||
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
|
||||
then
|
||||
cat <<\EOF
|
||||
Error: Attempt to add a non-ASCII file name.
|
||||
|
||||
This can cause problems if you want to work with people on other platforms.
|
||||
|
||||
To be portable it is advisable to rename the file.
|
||||
|
||||
If you know what you are doing you can disable this check using:
|
||||
|
||||
git config hooks.allownonascii true
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If there are whitespace errors, print the offending file names and fail.
|
||||
exec git diff-index --check --cached $against --
|
|
@ -0,0 +1,53 @@
|
|||
#!/bin/sh
|
||||
|
||||
# An example hook script to verify what is about to be pushed. Called by "git
|
||||
# push" after it has checked the remote status, but before anything has been
|
||||
# pushed. If this script exits with a non-zero status nothing will be pushed.
|
||||
#
|
||||
# This hook is called with the following parameters:
|
||||
#
|
||||
# $1 -- Name of the remote to which the push is being done
|
||||
# $2 -- URL to which the push is being done
|
||||
#
|
||||
# If pushing without using a named remote those arguments will be equal.
|
||||
#
|
||||
# Information about the commits which are being pushed is supplied as lines to
|
||||
# the standard input in the form:
|
||||
#
|
||||
# <local ref> <local sha1> <remote ref> <remote sha1>
|
||||
#
|
||||
# This sample shows how to prevent push of commits where the log message starts
|
||||
# with "WIP" (work in progress).
|
||||
|
||||
remote="$1"
|
||||
url="$2"
|
||||
|
||||
z40=0000000000000000000000000000000000000000
|
||||
|
||||
while read local_ref local_sha remote_ref remote_sha
|
||||
do
|
||||
if [ "$local_sha" = $z40 ]
|
||||
then
|
||||
# Handle delete
|
||||
:
|
||||
else
|
||||
if [ "$remote_sha" = $z40 ]
|
||||
then
|
||||
# New branch, examine all commits
|
||||
range="$local_sha"
|
||||
else
|
||||
# Update to existing branch, examine new commits
|
||||
range="$remote_sha..$local_sha"
|
||||
fi
|
||||
|
||||
# Check for WIP commit
|
||||
commit=`git rev-list -n 1 --grep '^WIP' "$range"`
|
||||
if [ -n "$commit" ]
|
||||
then
|
||||
echo >&2 "Found WIP commit in $local_ref, not pushing"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,169 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2006, 2008 Junio C Hamano
|
||||
#
|
||||
# The "pre-rebase" hook is run just before "git rebase" starts doing
|
||||
# its job, and can prevent the command from running by exiting with
|
||||
# non-zero status.
|
||||
#
|
||||
# The hook is called with the following parameters:
|
||||
#
|
||||
# $1 -- the upstream the series was forked from.
|
||||
# $2 -- the branch being rebased (or empty when rebasing the current branch).
|
||||
#
|
||||
# This sample shows how to prevent topic branches that are already
|
||||
# merged to 'next' branch from getting rebased, because allowing it
|
||||
# would result in rebasing already published history.
|
||||
|
||||
publish=next
|
||||
basebranch="$1"
|
||||
if test "$#" = 2
|
||||
then
|
||||
topic="refs/heads/$2"
|
||||
else
|
||||
topic=`git symbolic-ref HEAD` ||
|
||||
exit 0 ;# we do not interrupt rebasing detached HEAD
|
||||
fi
|
||||
|
||||
case "$topic" in
|
||||
refs/heads/??/*)
|
||||
;;
|
||||
*)
|
||||
exit 0 ;# we do not interrupt others.
|
||||
;;
|
||||
esac
|
||||
|
||||
# Now we are dealing with a topic branch being rebased
|
||||
# on top of master. Is it OK to rebase it?
|
||||
|
||||
# Does the topic really exist?
|
||||
git show-ref -q "$topic" || {
|
||||
echo >&2 "No such branch $topic"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Is topic fully merged to master?
|
||||
not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
|
||||
if test -z "$not_in_master"
|
||||
then
|
||||
echo >&2 "$topic is fully merged to master; better remove it."
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
fi
|
||||
|
||||
# Is topic ever merged to next? If so you should not be rebasing it.
|
||||
only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
|
||||
only_next_2=`git rev-list ^master ${publish} | sort`
|
||||
if test "$only_next_1" = "$only_next_2"
|
||||
then
|
||||
not_in_topic=`git rev-list "^$topic" master`
|
||||
if test -z "$not_in_topic"
|
||||
then
|
||||
echo >&2 "$topic is already up-to-date with master"
|
||||
exit 1 ;# we could allow it, but there is no point.
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
else
|
||||
not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
|
||||
/usr/bin/perl -e '
|
||||
my $topic = $ARGV[0];
|
||||
my $msg = "* $topic has commits already merged to public branch:\n";
|
||||
my (%not_in_next) = map {
|
||||
/^([0-9a-f]+) /;
|
||||
($1 => 1);
|
||||
} split(/\n/, $ARGV[1]);
|
||||
for my $elem (map {
|
||||
/^([0-9a-f]+) (.*)$/;
|
||||
[$1 => $2];
|
||||
} split(/\n/, $ARGV[2])) {
|
||||
if (!exists $not_in_next{$elem->[0]}) {
|
||||
if ($msg) {
|
||||
print STDERR $msg;
|
||||
undef $msg;
|
||||
}
|
||||
print STDERR " $elem->[1]\n";
|
||||
}
|
||||
}
|
||||
' "$topic" "$not_in_next" "$not_in_master"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
################################################################
|
||||
|
||||
This sample hook safeguards topic branches that have been
|
||||
published from being rewound.
|
||||
|
||||
The workflow assumed here is:
|
||||
|
||||
* Once a topic branch forks from "master", "master" is never
|
||||
merged into it again (either directly or indirectly).
|
||||
|
||||
* Once a topic branch is fully cooked and merged into "master",
|
||||
it is deleted. If you need to build on top of it to correct
|
||||
earlier mistakes, a new topic branch is created by forking at
|
||||
the tip of the "master". This is not strictly necessary, but
|
||||
it makes it easier to keep your history simple.
|
||||
|
||||
* Whenever you need to test or publish your changes to topic
|
||||
branches, merge them into "next" branch.
|
||||
|
||||
The script, being an example, hardcodes the publish branch name
|
||||
to be "next", but it is trivial to make it configurable via
|
||||
$GIT_DIR/config mechanism.
|
||||
|
||||
With this workflow, you would want to know:
|
||||
|
||||
(1) ... if a topic branch has ever been merged to "next". Young
|
||||
topic branches can have stupid mistakes you would rather
|
||||
clean up before publishing, and things that have not been
|
||||
merged into other branches can be easily rebased without
|
||||
affecting other people. But once it is published, you would
|
||||
not want to rewind it.
|
||||
|
||||
(2) ... if a topic branch has been fully merged to "master".
|
||||
Then you can delete it. More importantly, you should not
|
||||
build on top of it -- other people may already want to
|
||||
change things related to the topic as patches against your
|
||||
"master", so if you need further changes, it is better to
|
||||
fork the topic (perhaps with the same name) afresh from the
|
||||
tip of "master".
|
||||
|
||||
Let's look at this example:
|
||||
|
||||
o---o---o---o---o---o---o---o---o---o "next"
|
||||
/ / / /
|
||||
/ a---a---b A / /
|
||||
/ / / /
|
||||
/ / c---c---c---c B /
|
||||
/ / / \ /
|
||||
/ / / b---b C \ /
|
||||
/ / / / \ /
|
||||
---o---o---o---o---o---o---o---o---o---o---o "master"
|
||||
|
||||
|
||||
A, B and C are topic branches.
|
||||
|
||||
* A has one fix since it was merged up to "next".
|
||||
|
||||
* B has finished. It has been fully merged up to "master" and "next",
|
||||
and is ready to be deleted.
|
||||
|
||||
* C has not merged to "next" at all.
|
||||
|
||||
We would want to allow C to be rebased, refuse A, and encourage
|
||||
B to be deleted.
|
||||
|
||||
To compute (1):
|
||||
|
||||
git rev-list ^master ^topic next
|
||||
git rev-list ^master next
|
||||
|
||||
if these match, topic has not merged in next at all.
|
||||
|
||||
To compute (2):
|
||||
|
||||
git rev-list master..topic
|
||||
|
||||
if this is empty, it is fully merged to "master".
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to prepare the commit log message.
|
||||
# Called by "git commit" with the name of the file that has the
|
||||
# commit message, followed by the description of the commit
|
||||
# message's source. The hook's purpose is to edit the commit
|
||||
# message file. If the hook fails with a non-zero status,
|
||||
# the commit is aborted.
|
||||
#
|
||||
# To enable this hook, rename this file to "prepare-commit-msg".
|
||||
|
||||
# This hook includes three examples. The first comments out the
|
||||
# "Conflicts:" part of a merge commit.
|
||||
#
|
||||
# The second includes the output of "git diff --name-status -r"
|
||||
# into the message, just before the "git status" output. It is
|
||||
# commented because it doesn't cope with --amend or with squashed
|
||||
# commits.
|
||||
#
|
||||
# The third example adds a Signed-off-by line to the message, that can
|
||||
# still be edited. This is rarely a good idea.
|
||||
|
||||
case "$2,$3" in
|
||||
merge,)
|
||||
/usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
|
||||
|
||||
# ,|template,)
|
||||
# /usr/bin/perl -i.bak -pe '
|
||||
# print "\n" . `git diff --cached --name-status -r`
|
||||
# if /^#/ && $first++ == 0' "$1" ;;
|
||||
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
|
||||
# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
|
|
@ -0,0 +1,128 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook script to blocks unannotated tags from entering.
|
||||
# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
|
||||
#
|
||||
# To enable this hook, rename this file to "update".
|
||||
#
|
||||
# Config
|
||||
# ------
|
||||
# hooks.allowunannotated
|
||||
# This boolean sets whether unannotated tags will be allowed into the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowdeletetag
|
||||
# This boolean sets whether deleting tags will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.allowmodifytag
|
||||
# This boolean sets whether a tag may be modified after creation. By default
|
||||
# it won't be.
|
||||
# hooks.allowdeletebranch
|
||||
# This boolean sets whether deleting branches will be allowed in the
|
||||
# repository. By default they won't be.
|
||||
# hooks.denycreatebranch
|
||||
# This boolean sets whether remotely creating branches will be denied
|
||||
# in the repository. By default this is allowed.
|
||||
#
|
||||
|
||||
# --- Command line
|
||||
refname="$1"
|
||||
oldrev="$2"
|
||||
newrev="$3"
|
||||
|
||||
# --- Safety check
|
||||
if [ -z "$GIT_DIR" ]; then
|
||||
echo "Don't run this script from the command line." >&2
|
||||
echo " (if you want, you could supply GIT_DIR then run" >&2
|
||||
echo " $0 <ref> <oldrev> <newrev>)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
|
||||
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Config
|
||||
allowunannotated=$(git config --bool hooks.allowunannotated)
|
||||
allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
|
||||
denycreatebranch=$(git config --bool hooks.denycreatebranch)
|
||||
allowdeletetag=$(git config --bool hooks.allowdeletetag)
|
||||
allowmodifytag=$(git config --bool hooks.allowmodifytag)
|
||||
|
||||
# check for no description
|
||||
projectdesc=$(sed -e '1q' "$GIT_DIR/description")
|
||||
case "$projectdesc" in
|
||||
"Unnamed repository"* | "")
|
||||
echo "*** Project description file hasn't been set" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Check types
|
||||
# if $newrev is 0000...0000, it's a commit to delete a ref.
|
||||
zero="0000000000000000000000000000000000000000"
|
||||
if [ "$newrev" = "$zero" ]; then
|
||||
newrev_type=delete
|
||||
else
|
||||
newrev_type=$(git cat-file -t $newrev)
|
||||
fi
|
||||
|
||||
case "$refname","$newrev_type" in
|
||||
refs/tags/*,commit)
|
||||
# un-annotated tag
|
||||
short_refname=${refname##refs/tags/}
|
||||
if [ "$allowunannotated" != "true" ]; then
|
||||
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
|
||||
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,delete)
|
||||
# delete tag
|
||||
if [ "$allowdeletetag" != "true" ]; then
|
||||
echo "*** Deleting a tag is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/tags/*,tag)
|
||||
# annotated tag
|
||||
if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
|
||||
then
|
||||
echo "*** Tag '$refname' already exists." >&2
|
||||
echo "*** Modifying a tag is not allowed in this repository." >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,commit)
|
||||
# branch
|
||||
if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
|
||||
echo "*** Creating a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/heads/*,delete)
|
||||
# delete branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
refs/remotes/*,commit)
|
||||
# tracking branch
|
||||
;;
|
||||
refs/remotes/*,delete)
|
||||
# delete tracking branch
|
||||
if [ "$allowdeletebranch" != "true" ]; then
|
||||
echo "*** Deleting a tracking branch is not allowed in this repository" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
# Anything else (is there anything else?)
|
||||
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# --- Finished
|
||||
exit 0
|
|
@ -0,0 +1,6 @@
|
|||
# git ls-files --others --exclude-from=.git/info/exclude
|
||||
# Lines that start with '#' are comments.
|
||||
# For a project mostly in C, the following would be a good set of
|
||||
# exclude patterns (uncomment them if you want to use them):
|
||||
# *.[oa]
|
||||
# *~
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
x•ÍM
|
||||
à @á®=ÅìÁñgœ<67>Rz£Æ5B0÷o®Ðíƒ<C3AD>—Fïmúð˜g)@(†eCâš )‹c‘Í$Y)ÊEÆ$FÅkîã„zÅ£x1“eã>µÇö]Òèo@g½g
|
||||
<EFBFBD>à©Yku×û5ËJµ£Mõíæ/0
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1 @@
|
|||
e1022324d23146d29075a3e7c6f637cb67f091b5
|
|
@ -0,0 +1,9 @@
|
|||
#! /usr/bin/env ruby
|
||||
$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
|
||||
require 'lib/git_http'
|
||||
config = {
|
||||
:project_root => "/opt",
|
||||
:upload_pack => true,
|
||||
:receive_pack => false,
|
||||
}
|
||||
Rack::Handler::FastCGI.run(GitHttp::App.new(config))
|
|
@ -0,0 +1,20 @@
|
|||
# -*- encoding: utf-8 -*-
|
||||
require File.expand_path('../lib/grack/version', __FILE__)
|
||||
|
||||
Gem::Specification.new do |gem|
|
||||
gem.authors = ["Scott Chacon"]
|
||||
gem.email = ["schacon@gmail.com"]
|
||||
gem.description = %q{Ruby/Rack Git Smart-HTTP Server Handler}
|
||||
gem.summary = %q{Ruby/Rack Git Smart-HTTP Server Handler}
|
||||
gem.homepage = "https://github.com/gitlabhq/grack"
|
||||
|
||||
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||
gem.files = `git ls-files`.split("\n")
|
||||
gem.test_files = `git ls-files -- tests/*`.split("\n")
|
||||
gem.name = "grack"
|
||||
gem.require_paths = ["lib"]
|
||||
gem.version = Grack::VERSION
|
||||
|
||||
gem.add_dependency("rack", "~> 1.4.5")
|
||||
gem.add_development_dependency("mocha", "~> 0.11")
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
Installation
|
||||
========================
|
||||
|
||||
** This documentation is not finished yet. I haven't tested all of
|
||||
these and it's obviously incomplete - these are currently just notes.
|
||||
|
||||
FastCGI
|
||||
---------------------------------------
|
||||
Here is an example config from lighttpd server:
|
||||
----
|
||||
# main fastcgi entry
|
||||
$HTTP["url"] =~ "^/myapp/.+$" {
|
||||
fastcgi.server = ( "/myapp" =>
|
||||
( "localhost" =>
|
||||
( "bin-path" => "/var/www/localhost/cgi-bin/dispatch.fcgi",
|
||||
"docroot" => "/var/www/localhost/htdocs/myapp",
|
||||
"host" => "127.0.0.1",
|
||||
"port" => 1026,
|
||||
"check-local" => "disable"
|
||||
)
|
||||
)
|
||||
)
|
||||
} # HTTP[url]
|
||||
----
|
||||
You can use the examples/dispatch.fcgi file as your dispatcher.
|
||||
|
||||
(Example Apache setup?)
|
||||
|
||||
Installing in a Java application server
|
||||
---------------------------------------
|
||||
# install Warbler
|
||||
$ sudo gem install warbler
|
||||
$ cd gitsmart
|
||||
$ (edit config.ru)
|
||||
$ warble
|
||||
$ cp gitsmart.war /path/to/java/autodeploy/dir
|
||||
|
||||
Unicorn
|
||||
---------------------------------------
|
||||
With Unicorn (http://unicorn.bogomips.org/) you can just run 'unicorn'
|
||||
in the directory with the config.ru file.
|
||||
|
||||
Thin
|
||||
---------------------------------------
|
||||
thin.yml
|
||||
---
|
||||
pid: /home/deploy/myapp/server/thin.pid
|
||||
log: /home/deploy/myapp/logs/thin.log
|
||||
timeout: 30
|
||||
port: 7654
|
||||
max_conns: 1024
|
||||
chdir: /home/deploy/myapp/site_files
|
||||
rackup: /home/deploy/myapp/server/config.ru
|
||||
max_persistent_conns: 512
|
||||
environment: production
|
||||
address: 127.0.0.1
|
||||
servers: 1
|
||||
daemonize: true
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
require "grack/bundle"
|
||||
|
||||
module Grack
|
||||
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
require 'rack/auth/basic'
|
||||
require 'rack/auth/abstract/handler'
|
||||
require 'rack/auth/abstract/request'
|
||||
|
||||
module Grack
|
||||
class Auth < Rack::Auth::Basic
|
||||
def call(env)
|
||||
@env = env
|
||||
@request = Rack::Request.new(env)
|
||||
@auth = Request.new(env)
|
||||
|
||||
if not @auth.provided?
|
||||
unauthorized
|
||||
elsif not @auth.basic?
|
||||
bad_request
|
||||
else
|
||||
result = if (access = valid?(@auth) and access == true)
|
||||
@env['REMOTE_USER'] = @auth.username
|
||||
@app.call(env)
|
||||
else
|
||||
if access == '404'
|
||||
render_not_found
|
||||
elsif access == '403'
|
||||
render_no_access
|
||||
else
|
||||
unauthorized
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end# method call
|
||||
|
||||
# def valid?
|
||||
# false
|
||||
# end
|
||||
end# class Auth
|
||||
end# module Grack
|
|
@ -0,0 +1,19 @@
|
|||
require 'rack/builder'
|
||||
require 'grack/auth'
|
||||
require 'grack/server'
|
||||
|
||||
module Grack
|
||||
module Bundle
|
||||
extend self
|
||||
|
||||
def new(config)
|
||||
Rack::Builder.new do
|
||||
use Grack::Auth do |username, password|
|
||||
yield(username, password)
|
||||
end
|
||||
run Grack::Server.new(config)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,85 @@
|
|||
module Grack
|
||||
class Git
|
||||
attr_reader :repo
|
||||
|
||||
def initialize(git_path, repo_path)
|
||||
@git_path = git_path
|
||||
@repo = repo_path
|
||||
end
|
||||
|
||||
def update_server_info
|
||||
execute(%W(update-server-info))
|
||||
end
|
||||
|
||||
def command(cmd)
|
||||
[@git_path || 'git'] + cmd
|
||||
end
|
||||
|
||||
def capture(cmd)
|
||||
# _Not_ the same as `IO.popen(...).read`
|
||||
# By using a block we tell IO.popen to close (wait for) the child process
|
||||
# after we are done reading its output.
|
||||
if RUBY_VERSION.start_with?("2")
|
||||
IO.popen(popen_env, cmd, popen_options) { |p| p.read }
|
||||
else
|
||||
IO.popen(cmd, popen_options) { |p| p.read }
|
||||
end
|
||||
end
|
||||
|
||||
def execute(cmd)
|
||||
cmd = command(cmd)
|
||||
if block_given?
|
||||
|
||||
if RUBY_VERSION.start_with?("2")
|
||||
IO.popen(popen_env, cmd, File::RDWR, popen_options) do |pipe|
|
||||
yield(pipe)
|
||||
end
|
||||
else
|
||||
IO.popen(cmd, File::RDWR, popen_options) do |pipe|
|
||||
yield(pipe)
|
||||
end
|
||||
end
|
||||
else
|
||||
capture(cmd).chomp
|
||||
end
|
||||
end
|
||||
|
||||
def popen_options
|
||||
{ chdir: repo, unsetenv_others: true }
|
||||
end
|
||||
|
||||
def popen_env
|
||||
{ 'PATH' => ENV['PATH'], 'GL_ID' => ENV['GL_ID'] }
|
||||
end
|
||||
|
||||
def config_setting(service_name)
|
||||
service_name = service_name.gsub('-', '')
|
||||
setting = config("http.#{service_name}")
|
||||
|
||||
if service_name == 'uploadpack'
|
||||
setting != 'false'
|
||||
else
|
||||
setting == 'true'
|
||||
end
|
||||
end
|
||||
|
||||
def config(config_name)
|
||||
execute(%W(config #{config_name}))
|
||||
end
|
||||
|
||||
def valid_repo?
|
||||
return false unless File.exists?(repo) && File.realpath(repo) == repo
|
||||
|
||||
match = execute(%W(rev-parse --git-dir)).match(/\.$|\.git$/)
|
||||
|
||||
if RUBY_VERSION.start_with?("2")
|
||||
if match.to_s == '.git'
|
||||
# Since the parent could be a git repo, we want to make sure the actual repo contains a git dir.
|
||||
return false unless Dir.entries(repo).include?('.git')
|
||||
end
|
||||
end
|
||||
|
||||
match
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,308 @@
|
|||
require 'zlib'
|
||||
require 'rack/request'
|
||||
require 'rack/response'
|
||||
require 'rack/utils'
|
||||
require 'time'
|
||||
|
||||
require 'grack/git'
|
||||
|
||||
module Grack
|
||||
class Server
|
||||
attr_reader :git
|
||||
|
||||
SERVICES = [
|
||||
["POST", 'service_rpc', "(.*?)/git-upload-pack$", 'upload-pack'],
|
||||
["POST", 'service_rpc', "(.*?)/git-receive-pack$", 'receive-pack'],
|
||||
|
||||
["GET", 'get_info_refs', "(.*?)/info/refs$"],
|
||||
["GET", 'get_text_file', "(.*?)/HEAD$"],
|
||||
["GET", 'get_text_file', "(.*?)/objects/info/alternates$"],
|
||||
["GET", 'get_text_file', "(.*?)/objects/info/http-alternates$"],
|
||||
["GET", 'get_info_packs', "(.*?)/objects/info/packs$"],
|
||||
["GET", 'get_text_file', "(.*?)/objects/info/[^/]*$"],
|
||||
["GET", 'get_loose_object', "(.*?)/objects/[0-9a-f]{2}/[0-9a-f]{38}$"],
|
||||
["GET", 'get_pack_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.pack$"],
|
||||
["GET", 'get_idx_file', "(.*?)/objects/pack/pack-[0-9a-f]{40}\\.idx$"],
|
||||
]
|
||||
|
||||
def initialize(config = false)
|
||||
set_config(config)
|
||||
end
|
||||
|
||||
def set_config(config)
|
||||
@config = config || {}
|
||||
end
|
||||
|
||||
def set_config_setting(key, value)
|
||||
@config[key] = value
|
||||
end
|
||||
|
||||
def call(env)
|
||||
dup._call(env)
|
||||
end
|
||||
|
||||
def _call(env)
|
||||
@env = env
|
||||
@req = Rack::Request.new(env)
|
||||
|
||||
cmd, path, @reqfile, @rpc = match_routing
|
||||
|
||||
return render_method_not_allowed if cmd == 'not_allowed'
|
||||
return render_not_found unless cmd
|
||||
|
||||
@git = get_git(env["REP_PATH"] || path)
|
||||
return render_not_found unless git.valid_repo?
|
||||
|
||||
self.method(cmd).call
|
||||
end
|
||||
|
||||
# ---------------------------------
|
||||
# actual command handling functions
|
||||
# ---------------------------------
|
||||
|
||||
# Uses chunked (streaming) transfer, otherwise response
|
||||
# blocks to calculate Content-Length header
|
||||
# http://en.wikipedia.org/wiki/Chunked_transfer_encoding
|
||||
|
||||
CRLF = "\r\n"
|
||||
|
||||
def service_rpc
|
||||
return render_no_access unless has_access?(@rpc, true)
|
||||
|
||||
input = read_body
|
||||
|
||||
@res = Rack::Response.new
|
||||
@res.status = 200
|
||||
@res["Content-Type"] = "application/x-git-%s-result" % @rpc
|
||||
@res["Transfer-Encoding"] = "chunked"
|
||||
@res["Cache-Control"] = "no-cache"
|
||||
|
||||
@res.finish do
|
||||
git.execute([@rpc, '--stateless-rpc', git.repo]) do |pipe|
|
||||
pipe.write(input)
|
||||
pipe.close_write
|
||||
|
||||
while block = pipe.read(8192) # 8KB at a time
|
||||
@res.write encode_chunk(block) # stream it to the client
|
||||
end
|
||||
|
||||
@res.write terminating_chunk
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def encode_chunk(chunk)
|
||||
size_in_hex = chunk.size.to_s(16)
|
||||
[size_in_hex, CRLF, chunk, CRLF].join
|
||||
end
|
||||
|
||||
def terminating_chunk
|
||||
[0, CRLF, CRLF].join
|
||||
end
|
||||
|
||||
def get_info_refs
|
||||
service_name = get_service_type
|
||||
return dumb_info_refs unless has_access?(service_name)
|
||||
|
||||
refs = git.execute([service_name, '--stateless-rpc', '--advertise-refs', git.repo])
|
||||
|
||||
@res = Rack::Response.new
|
||||
@res.status = 200
|
||||
@res["Content-Type"] = "application/x-git-%s-advertisement" % service_name
|
||||
hdr_nocache
|
||||
|
||||
@res.write(pkt_write("# service=git-#{service_name}\n"))
|
||||
@res.write(pkt_flush)
|
||||
@res.write(refs)
|
||||
|
||||
@res.finish
|
||||
end
|
||||
|
||||
def dumb_info_refs
|
||||
git.update_server_info
|
||||
send_file(@reqfile, "text/plain; charset=utf-8") do
|
||||
hdr_nocache
|
||||
end
|
||||
end
|
||||
|
||||
def get_info_packs
|
||||
# objects/info/packs
|
||||
send_file(@reqfile, "text/plain; charset=utf-8") do
|
||||
hdr_nocache
|
||||
end
|
||||
end
|
||||
|
||||
def get_loose_object
|
||||
send_file(@reqfile, "application/x-git-loose-object") do
|
||||
hdr_cache_forever
|
||||
end
|
||||
end
|
||||
|
||||
def get_pack_file
|
||||
send_file(@reqfile, "application/x-git-packed-objects") do
|
||||
hdr_cache_forever
|
||||
end
|
||||
end
|
||||
|
||||
def get_idx_file
|
||||
send_file(@reqfile, "application/x-git-packed-objects-toc") do
|
||||
hdr_cache_forever
|
||||
end
|
||||
end
|
||||
|
||||
def get_text_file
|
||||
send_file(@reqfile, "text/plain") do
|
||||
hdr_nocache
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------
|
||||
# logic helping functions
|
||||
# ------------------------
|
||||
|
||||
# some of this borrowed from the Rack::File implementation
|
||||
def send_file(reqfile, content_type)
|
||||
reqfile = File.join(git.repo, reqfile)
|
||||
return render_not_found unless File.exists?(reqfile)
|
||||
|
||||
return render_not_found unless reqfile == File.realpath(reqfile)
|
||||
|
||||
# reqfile looks legit: no path traversal, no leading '|'
|
||||
|
||||
@res = Rack::Response.new
|
||||
@res.status = 200
|
||||
@res["Content-Type"] = content_type
|
||||
@res["Last-Modified"] = File.mtime(reqfile).httpdate
|
||||
|
||||
yield
|
||||
|
||||
if size = File.size?(reqfile)
|
||||
@res["Content-Length"] = size.to_s
|
||||
@res.finish do
|
||||
File.open(reqfile, "rb") do |file|
|
||||
while part = file.read(8192)
|
||||
@res.write part
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
body = [File.read(reqfile)]
|
||||
size = Rack::Utils.bytesize(body.first)
|
||||
@res["Content-Length"] = size
|
||||
@res.write body
|
||||
@res.finish
|
||||
end
|
||||
end
|
||||
|
||||
def get_git(path)
|
||||
# root = @config[:project_root] || Dir.pwd
|
||||
# path = File.join(root, path)
|
||||
Grack::Git.new(@config[:git_path], path)
|
||||
end
|
||||
|
||||
def get_service_type
|
||||
service_type = @req.params['service']
|
||||
return false unless service_type
|
||||
return false if service_type[0, 4] != 'git-'
|
||||
service_type.gsub('git-', '')
|
||||
end
|
||||
|
||||
def match_routing
|
||||
cmd = nil
|
||||
path = nil
|
||||
|
||||
SERVICES.each do |method, handler, match, rpc|
|
||||
next unless m = Regexp.new(match).match(@req.path_info)
|
||||
|
||||
return ['not_allowed'] unless method == @req.request_method
|
||||
|
||||
cmd = handler
|
||||
path = m[1]
|
||||
file = @req.path_info.sub(path + '/', '')
|
||||
|
||||
return [cmd, path, file, rpc]
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def has_access?(rpc, check_content_type = false)
|
||||
if check_content_type
|
||||
conten_type = "application/x-git-%s-request" % rpc
|
||||
return false unless @req.content_type == conten_type
|
||||
end
|
||||
|
||||
return false unless ['upload-pack', 'receive-pack'].include?(rpc)
|
||||
|
||||
if rpc == 'receive-pack'
|
||||
return @config[:receive_pack] if @config.include?(:receive_pack)
|
||||
end
|
||||
|
||||
if rpc == 'upload-pack'
|
||||
return @config[:upload_pack] if @config.include?(:upload_pack)
|
||||
end
|
||||
|
||||
git.config_setting(rpc)
|
||||
end
|
||||
|
||||
def read_body
|
||||
if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
|
||||
Zlib::GzipReader.new(@req.body).read
|
||||
else
|
||||
@req.body.read
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------
|
||||
# HTTP error response handling functions
|
||||
# --------------------------------------
|
||||
|
||||
PLAIN_TYPE = { "Content-Type" => "text/plain" }
|
||||
|
||||
def render_method_not_allowed
|
||||
if @env['SERVER_PROTOCOL'] == "HTTP/1.1"
|
||||
[405, PLAIN_TYPE, ["Method Not Allowed"]]
|
||||
else
|
||||
[400, PLAIN_TYPE, ["Bad Request"]]
|
||||
end
|
||||
end
|
||||
|
||||
def render_not_found
|
||||
[404, PLAIN_TYPE, ["Not Found"]]
|
||||
end
|
||||
|
||||
def render_no_access
|
||||
[403, PLAIN_TYPE, ["Forbidden"]]
|
||||
end
|
||||
|
||||
|
||||
# ------------------------------
|
||||
# packet-line handling functions
|
||||
# ------------------------------
|
||||
|
||||
def pkt_flush
|
||||
'0000'
|
||||
end
|
||||
|
||||
def pkt_write(str)
|
||||
(str.size + 4).to_s(16).rjust(4, '0') + str
|
||||
end
|
||||
|
||||
# ------------------------
|
||||
# header writing functions
|
||||
# ------------------------
|
||||
|
||||
def hdr_nocache
|
||||
@res["Expires"] = "Fri, 01 Jan 1980 00:00:00 GMT"
|
||||
@res["Pragma"] = "no-cache"
|
||||
@res["Cache-Control"] = "no-cache, max-age=0, must-revalidate"
|
||||
end
|
||||
|
||||
def hdr_cache_forever
|
||||
now = Time.now().to_i
|
||||
@res["Date"] = now.to_s
|
||||
@res["Expires"] = (now + 31536000).to_s;
|
||||
@res["Cache-Control"] = "public, max-age=31536000";
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,3 @@
|
|||
module Grack
|
||||
VERSION = "2.0.2"
|
||||
end
|
|
@ -0,0 +1,264 @@
|
|||
require 'rack'
|
||||
require 'rack/test'
|
||||
require 'test/unit'
|
||||
require 'mocha'
|
||||
require 'digest/sha1'
|
||||
|
||||
require_relative '../lib/grack/server.rb'
|
||||
require_relative '../lib/grack/git.rb'
|
||||
require 'pp'
|
||||
|
||||
class GitHttpTest < Test::Unit::TestCase
|
||||
include Rack::Test::Methods
|
||||
|
||||
def example
|
||||
File.expand_path(File.dirname(__FILE__))
|
||||
end
|
||||
|
||||
def app
|
||||
config = {
|
||||
:project_root => example,
|
||||
:upload_pack => true,
|
||||
:receive_pack => true,
|
||||
}
|
||||
Grack::Server.new(config)
|
||||
end
|
||||
|
||||
def test_upload_pack_advertisement
|
||||
get "/example/info/refs?service=git-upload-pack"
|
||||
assert_equal 200, r.status
|
||||
assert_equal "application/x-git-upload-pack-advertisement", r.headers["Content-Type"]
|
||||
assert_equal "001e# service=git-upload-pack", r.body.split("\n").first
|
||||
assert_match 'multi_ack_detailed', r.body
|
||||
end
|
||||
|
||||
def test_no_access_wrong_content_type_up
|
||||
post "/example/git-upload-pack"
|
||||
assert_equal 403, r.status
|
||||
end
|
||||
|
||||
def test_no_access_wrong_content_type_rp
|
||||
post "/example/git-receive-pack"
|
||||
assert_equal 403, r.status
|
||||
end
|
||||
|
||||
def test_no_access_wrong_method_rcp
|
||||
get "/example/git-upload-pack"
|
||||
assert_equal 400, r.status
|
||||
end
|
||||
|
||||
def test_no_access_wrong_command_rcp
|
||||
post "/example/git-upload-packfile"
|
||||
assert_equal 404, r.status
|
||||
end
|
||||
|
||||
def test_no_access_wrong_path_rcp
|
||||
Grack::Git.any_instance.stubs(:valid_repo?).returns(false)
|
||||
post "/example-wrong/git-upload-pack"
|
||||
assert_equal 404, r.status
|
||||
end
|
||||
|
||||
def test_upload_pack_rpc
|
||||
Grack::Git.any_instance.stubs(:valid_repo?).returns(true)
|
||||
IO.stubs(:popen).returns(MockProcess.new)
|
||||
post "/example/git-upload-pack", {}, {"CONTENT_TYPE" => "application/x-git-upload-pack-request"}
|
||||
assert_equal 200, r.status
|
||||
assert_equal "application/x-git-upload-pack-result", r.headers["Content-Type"]
|
||||
end
|
||||
|
||||
def test_receive_pack_advertisement
|
||||
get "/example/info/refs?service=git-receive-pack"
|
||||
assert_equal 200, r.status
|
||||
assert_equal "application/x-git-receive-pack-advertisement", r.headers["Content-Type"]
|
||||
assert_equal "001f# service=git-receive-pack", r.body.split("\n").first
|
||||
assert_match 'report-status', r.body
|
||||
assert_match 'delete-refs', r.body
|
||||
assert_match 'ofs-delta', r.body
|
||||
end
|
||||
|
||||
def test_recieve_pack_rpc
|
||||
Grack::Git.any_instance.stubs(:valid_repo?).returns(true)
|
||||
IO.stubs(:popen).yields(MockProcess.new)
|
||||
post "/example/git-receive-pack", {}, {"CONTENT_TYPE" => "application/x-git-receive-pack-request"}
|
||||
assert_equal 200, r.status
|
||||
assert_equal "application/x-git-receive-pack-result", r.headers["Content-Type"]
|
||||
end
|
||||
|
||||
def test_info_refs_dumb
|
||||
get "/example/.git/info/refs"
|
||||
assert_equal 200, r.status
|
||||
end
|
||||
|
||||
def test_info_packs
|
||||
get "/example/.git/objects/info/packs"
|
||||
assert_equal 200, r.status
|
||||
assert_match /P pack-(.*?).pack/, r.body
|
||||
end
|
||||
|
||||
def test_loose_objects
|
||||
path, content = write_test_objects
|
||||
get "/example/.git/objects/#{path}"
|
||||
assert_equal 200, r.status
|
||||
assert_equal content, r.body
|
||||
remove_test_objects
|
||||
end
|
||||
|
||||
def test_pack_file
|
||||
path, content = write_test_objects
|
||||
get "/example/.git/objects/pack/pack-#{content}.pack"
|
||||
assert_equal 200, r.status
|
||||
assert_equal content, r.body
|
||||
remove_test_objects
|
||||
end
|
||||
|
||||
def test_index_file
|
||||
path, content = write_test_objects
|
||||
get "/example/.git/objects/pack/pack-#{content}.idx"
|
||||
assert_equal 200, r.status
|
||||
assert_equal content, r.body
|
||||
remove_test_objects
|
||||
end
|
||||
|
||||
def test_text_file
|
||||
get "/example/.git/HEAD"
|
||||
assert_equal 200, r.status
|
||||
assert_equal 41, r.body.size # submodules have detached head
|
||||
end
|
||||
|
||||
def test_no_size_avail
|
||||
File.stubs('size?').returns(false)
|
||||
get "/example/.git/HEAD"
|
||||
assert_equal 200, r.status
|
||||
assert_equal 46, r.body.size # submodules have detached head
|
||||
end
|
||||
|
||||
def test_config_upload_pack_off
|
||||
a1 = app
|
||||
a1.set_config_setting(:upload_pack, false)
|
||||
session = Rack::Test::Session.new(a1)
|
||||
session.get "/example/info/refs?service=git-upload-pack"
|
||||
assert_equal 404, session.last_response.status
|
||||
end
|
||||
|
||||
def test_config_receive_pack_off
|
||||
a1 = app
|
||||
a1.set_config_setting(:receive_pack, false)
|
||||
session = Rack::Test::Session.new(a1)
|
||||
session.get "/example/info/refs?service=git-receive-pack"
|
||||
assert_equal 404, session.last_response.status
|
||||
end
|
||||
|
||||
def test_config_bad_service
|
||||
get "/example/info/refs?service=git-receive-packfile"
|
||||
assert_equal 404, r.status
|
||||
end
|
||||
|
||||
def test_git_config_receive_pack
|
||||
app1 = Grack::Server.new({:project_root => example})
|
||||
app1.instance_variable_set(:@git, Grack::Git.new('git', example ))
|
||||
session = Rack::Test::Session.new(app1)
|
||||
git = Grack::Git
|
||||
git.any_instance.stubs(:config).with('http.receivepack').returns('')
|
||||
session.get "/example/info/refs?service=git-receive-pack"
|
||||
assert_equal 404, session.last_response.status
|
||||
|
||||
git.any_instance.stubs(:config).with('http.receivepack').returns('true')
|
||||
session.get "/example/info/refs?service=git-receive-pack"
|
||||
assert_equal 200, session.last_response.status
|
||||
|
||||
git.any_instance.stubs(:config).with('http.receivepack').returns('false')
|
||||
session.get "/example/info/refs?service=git-receive-pack"
|
||||
assert_equal 404, session.last_response.status
|
||||
end
|
||||
|
||||
def test_git_config_upload_pack
|
||||
app1 = Grack::Server.new({:project_root => example})
|
||||
# app1.instance_variable_set(:@git, Grack::Git.new('git', example ))
|
||||
session = Rack::Test::Session.new(app1)
|
||||
git = Grack::Git
|
||||
git.any_instance.stubs(:config).with('http.uploadpack').returns('')
|
||||
session.get "/example/info/refs?service=git-upload-pack"
|
||||
assert_equal 200, session.last_response.status
|
||||
|
||||
git.any_instance.stubs(:config).with('http.uploadpack').returns('true')
|
||||
session.get "/example/info/refs?service=git-upload-pack"
|
||||
assert_equal 200, session.last_response.status
|
||||
|
||||
git.any_instance.stubs(:config).with('http.uploadpack').returns('false')
|
||||
session.get "/example/info/refs?service=git-upload-pack"
|
||||
assert_equal 404, session.last_response.status
|
||||
end
|
||||
|
||||
def test_send_file
|
||||
app1 = app
|
||||
app1.instance_variable_set(:@git, Grack::Git.new('git', Dir.pwd))
|
||||
# Reject path traversal
|
||||
assert_equal 404, app1.send_file('tests/../tests', 'text/plain').first
|
||||
# Reject paths starting with '|', avoid File.read('|touch /tmp/pawned; ls /tmp')
|
||||
assert_equal 404, app1.send_file('|tests', 'text/plain').first
|
||||
end
|
||||
|
||||
def test_get_git
|
||||
# Guard against non-existent directories
|
||||
git1 = Grack::Git.new('git', 'foobar')
|
||||
assert_equal false, git1.valid_repo?
|
||||
# Guard against path traversal
|
||||
git2 = Grack::Git.new('git', '/../tests')
|
||||
assert_equal false, git2.valid_repo?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def r
|
||||
last_response
|
||||
end
|
||||
|
||||
def write_test_objects
|
||||
content = Digest::SHA1.hexdigest('gitrocks')
|
||||
base = File.join(File.expand_path(File.dirname(__FILE__)), 'example', '.git', 'objects')
|
||||
obj = File.join(base, '20')
|
||||
Dir.mkdir(obj) rescue nil
|
||||
file = File.join(obj, content[0, 38])
|
||||
File.open(file, 'w') { |f| f.write(content) }
|
||||
pack = File.join(base, 'pack', "pack-#{content}.pack")
|
||||
File.open(pack, 'w') { |f| f.write(content) }
|
||||
idx = File.join(base, 'pack', "pack-#{content}.idx")
|
||||
File.open(idx, 'w') { |f| f.write(content) }
|
||||
["20/#{content[0,38]}", content]
|
||||
end
|
||||
|
||||
def remove_test_objects
|
||||
content = Digest::SHA1.hexdigest('gitrocks')
|
||||
base = File.join(File.expand_path(File.dirname(__FILE__)), 'example', '.git', 'objects')
|
||||
obj = File.join(base, '20')
|
||||
file = File.join(obj, content[0, 38])
|
||||
pack = File.join(base, 'pack', "pack-#{content}.pack")
|
||||
idx = File.join(base, 'pack', "pack-#{content}.idx")
|
||||
File.unlink(file)
|
||||
File.unlink(pack)
|
||||
File.unlink(idx)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class MockProcess
|
||||
def initialize
|
||||
@counter = 0
|
||||
end
|
||||
|
||||
def write(data)
|
||||
end
|
||||
|
||||
def read(data = nil)
|
||||
''
|
||||
end
|
||||
|
||||
def eof?
|
||||
@counter += 1
|
||||
@counter > 1 ? true : false
|
||||
end
|
||||
|
||||
def close_write
|
||||
true
|
||||
end
|
||||
end
|
|
@ -1,2 +1,3 @@
|
|||
require 'trustie/utils'
|
||||
require 'trustie/utils/image'
|
||||
require 'trustie/grack/grack'
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
#coding=utf-8
|
||||
#
|
||||
require 'rack/auth/basic'
|
||||
require 'rack/auth/abstract/handler'
|
||||
require 'rack/auth/abstract/request'
|
||||
|
||||
module Grack
|
||||
|
||||
class Auth < Rack::Auth::Basic
|
||||
DOWNLOAD_COMMANDS = %w{ git-upload-pack git-upload-archive }
|
||||
PUSH_COMMANDS = %w{ git-receive-pack }
|
||||
|
||||
attr_accessor :user, :repository
|
||||
def call(env)
|
||||
@env = env
|
||||
@request = Rack::Request.new(env)
|
||||
@auth = Request.new(env)
|
||||
|
||||
if not @auth.provided?
|
||||
unauthorized
|
||||
elsif not @auth.basic?
|
||||
bad_request
|
||||
else
|
||||
result = if (access = valid?(@auth) and access == true)
|
||||
@env['REMOTE_USER'] = @auth.username
|
||||
env['REP_PATH'] = repository.root_url
|
||||
@app.call(env)
|
||||
else
|
||||
if access == '404'
|
||||
render_not_found
|
||||
elsif access == '403'
|
||||
#render_no_access
|
||||
unauthorized
|
||||
else
|
||||
unauthorized
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
end# method call
|
||||
|
||||
|
||||
def render_not_found
|
||||
[404, {"Content-Type" => "text/plain"}, ["Not Found"]]
|
||||
end
|
||||
|
||||
def valid?(auth)
|
||||
self.repository = auth_rep
|
||||
return "404" unless repository
|
||||
username, password = auth.credentials
|
||||
self.user = auth_user(username, password)
|
||||
return '403' unless user
|
||||
access = auth_request
|
||||
puts "access #{access}"
|
||||
access
|
||||
end
|
||||
|
||||
def auth_rep
|
||||
rep = nil
|
||||
match = @request.path_info.match(/(\/.+\.git)\//)
|
||||
if match
|
||||
rep = Repository.where("root_url like ?", "%#{match[1]}").first
|
||||
end
|
||||
rep
|
||||
end
|
||||
|
||||
def auth_user(username, password)
|
||||
u, last_login_on = User.try_to_login(username, password)
|
||||
unless u && (u.member_of?(repository.project) || u.admin?)
|
||||
u = nil
|
||||
end
|
||||
u
|
||||
end
|
||||
|
||||
def auth_request
|
||||
case git_cmd
|
||||
when *DOWNLOAD_COMMANDS
|
||||
user != nil
|
||||
when *PUSH_COMMANDS
|
||||
unless user
|
||||
false
|
||||
else
|
||||
### 只有Manager和Development才有push权限
|
||||
repository.project.members.where(user_id: user.id).first.roles.any?{|r| r.name == 'Manager' || r.name == 'Developer'}
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def git_cmd
|
||||
if @request.get?
|
||||
@request.params['service']
|
||||
elsif @request.post?
|
||||
File.basename(@request.path)
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
end# class Auth
|
||||
end# module Grack
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
require_relative 'auth'
|
||||
|
||||
module Trustie
|
||||
module Grack
|
||||
|
||||
def self.new
|
||||
Rack::Builder.new do
|
||||
use ::Grack::Auth
|
||||
run ::Grack::Server.new(
|
||||
project_root: Redmine::Configuration['repository_root_path'] || "/home/pdl/redmine-2.3.2-0/apache2/htdocs",
|
||||
upload_pack: true,
|
||||
receive_pack:true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -5157,7 +5157,7 @@ KEditor.prototype = {
|
|||
}
|
||||
});
|
||||
} else {
|
||||
statusbar.last().css('visibility', 'hidden');
|
||||
if(statusbar.last()) {statusbar.last().css('visibility', 'hidden');}
|
||||
}
|
||||
}
|
||||
return self;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
$(function(){
|
||||
$("#RSide").css("min-height",$("#LSide").height()-40).css("padding","10px");
|
||||
|
||||
//头像相关
|
||||
$("#homepage_portrait_image").live("mouseover",function(){
|
||||
$("#edit_user_file_btn").show();
|
||||
$("#watch_user_btn").show();
|
||||
}).live("mouseout",function(){
|
||||
$("#edit_user_file_btn").hide();
|
||||
$("#watch_user_btn").hide();
|
||||
});
|
||||
});
|
||||
|
||||
//编辑个人简介
|
||||
function show_edit_user_introduction() {
|
||||
$("#user_brief_introduction_show").hide();
|
||||
$("#user_brief_introduction_edit").show();
|
||||
$("#user_brief_introduction_edit").focus();
|
||||
}
|
||||
|
||||
//编辑个人简介完成之后提交
|
||||
function edit_user_introduction(url){
|
||||
$.get(
|
||||
url,
|
||||
{ brief_introduction: $("#user_brief_introduction_edit").val() },
|
||||
function (data) {
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$(function(){
|
||||
$(".newsType").mouseover(function(){
|
||||
$(".resourcesIcon").css({background:"url(images/resource_icon_list.png) 0px -25px no-repeat"});
|
||||
});
|
||||
$(".newsType").mouseout(function(){
|
||||
$(".resourcesIcon").css({background:"url(images/resource_icon_list.png) 0px 0px no-repeat"});
|
||||
});
|
||||
$(".resourcesSelected").mouseover(function(){
|
||||
$(".resourcesIcon").css({background:"url(images/resource_icon_list.png) 0px -25px no-repeat"});
|
||||
});
|
||||
$(".resourcesSelected").mouseout(function(){
|
||||
$(".resourcesIcon").css({background:"url(images/resource_icon_list.png) 0px 0px no-repeat"});
|
||||
});
|
||||
});
|
||||
//个人动态 end
|
|
@ -1,33 +1,263 @@
|
|||
//个人动态
|
||||
$(function(){
|
||||
$("#RSide").css("min-height",$("#LSide").height()-40).css("padding","10px");
|
||||
function init_editor(params){
|
||||
var editor = params.kindutil.create(params.textarea, {
|
||||
resizeType : 1,minWidth:"1px",width:"100%",height:"80px",
|
||||
items:['emoticons'],
|
||||
afterChange:function(){//按键事件
|
||||
nh_check_field({content:this,contentmsg:params.contentmsg,textarea:params.textarea});
|
||||
},
|
||||
afterCreate:function(){
|
||||
var toolbar = $("div[class='ke-toolbar']",params.div_form);
|
||||
$(".ke-outline>.ke-toolbar-icon",toolbar).append('表情');
|
||||
params.toolbar_container.append(toolbar);
|
||||
}
|
||||
}).loadPlugin('paste');
|
||||
return editor;
|
||||
}
|
||||
|
||||
//头像相关
|
||||
$("#homepage_portrait_image").live("mouseover",function(){
|
||||
$("#edit_user_file_btn").show();
|
||||
$("#watch_user_btn").show();
|
||||
}).live("mouseout",function(){
|
||||
$("#edit_user_file_btn").hide();
|
||||
$("#watch_user_btn").hide();
|
||||
function nh_check_field(params){
|
||||
var result=true;
|
||||
if(params.content!=undefined){
|
||||
if(params.content.isEmpty()){
|
||||
result=false;
|
||||
}
|
||||
if(params.content.html()!=params.textarea.html() || params.issubmit==true){
|
||||
params.textarea.html(params.content.html());
|
||||
params.content.sync();
|
||||
if(params.content.isEmpty()){
|
||||
params.contentmsg.html('内容不能为空');
|
||||
params.contentmsg.css({color:'#ff0000'});
|
||||
}else{
|
||||
params.contentmsg.html('填写正确');
|
||||
params.contentmsg.css({color:'#008000'});
|
||||
}
|
||||
params.contentmsg.show();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function init_form(params){
|
||||
params.form.submit(function(){
|
||||
var flag = false;
|
||||
if(params.form.attr('data-remote') != undefined ){
|
||||
flag = true
|
||||
}
|
||||
var is_checked = nh_check_field({
|
||||
issubmit:true,
|
||||
content:params.editor,
|
||||
contentmsg:params.contentmsg,
|
||||
textarea:params.textarea
|
||||
});
|
||||
if(is_checked){
|
||||
if(flag){
|
||||
return true;
|
||||
}else{
|
||||
$(this)[0].submit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
function nh_reset_form(params){
|
||||
params.form[0].reset();
|
||||
params.textarea.empty();
|
||||
if(params.editor != undefined){
|
||||
params.editor.html(params.textarea.html());
|
||||
}
|
||||
params.contentmsg.hide();
|
||||
}
|
||||
|
||||
KindEditor.ready(function(K){
|
||||
$("a[nhname='reply_btn']").live('click',function(){
|
||||
var params = {};
|
||||
params.kindutil = K;
|
||||
params.container = $(this).parent().parent('div');
|
||||
params.div_form = $("div[nhname='div_form']",params.container);
|
||||
params.form = $("form",params.div_form);
|
||||
params.textarea = $("textarea[name='user_notes']",params.div_form);
|
||||
params.textarea.prev('div').css("height","60px");
|
||||
params.contentmsg = $("p[nhname='contentmsg']",params.div_form);
|
||||
params.toolbar_container = $("div[nhname='toolbar_container']",params.div_form);
|
||||
params.cancel_btn = $("a[nhname='cancel_btn']",params.div_form);
|
||||
params.submit_btn = $("a[nhname='submit_btn']",params.div_form);
|
||||
if(params.textarea.data('init') == undefined){
|
||||
params.editor = init_editor(params);
|
||||
init_form(params);
|
||||
params.cancel_btn.click(function(){
|
||||
nh_reset_form(params);
|
||||
toggleAndSettingWordsVal(params.div_form, params.textarea);
|
||||
});
|
||||
params.submit_btn.click(function(){
|
||||
params.form.submit();
|
||||
});
|
||||
params.textarea.data('init',1);
|
||||
}
|
||||
params.cancel_btn.click();
|
||||
setTimeout(function(){
|
||||
if(!params.div_form.is(':hidden')){
|
||||
params.textarea.show();
|
||||
params.textarea.focus();
|
||||
params.textarea.hide();
|
||||
}
|
||||
},300);
|
||||
});
|
||||
|
||||
$("a[nhname='sub_reply_btn']").live('click',function(){
|
||||
var params = {};
|
||||
params.kindutil = K;
|
||||
params.container = $(this).parent().parent('div');
|
||||
params.div_form = $("div[nhname='sub_div_form']",params.container);
|
||||
params.form = $("form",params.div_form);
|
||||
params.textarea = $("textarea[name='user_notes']",params.div_form);
|
||||
params.textarea.prev('div').css("height","60px");
|
||||
params.contentmsg = $("p[nhname='sub_contentmsg']",params.div_form);
|
||||
params.toolbar_container = $("div[nhname='sub_toolbar_container']",params.div_form);
|
||||
params.cancel_btn = $("a[nhname='sub_cancel_btn']",params.div_form);
|
||||
params.submit_btn = $("a[nhname='sub_submit_btn']",params.div_form);
|
||||
if(params.textarea.data('init') == undefined){
|
||||
params.editor = init_editor(params);
|
||||
init_form(params);
|
||||
params.cancel_btn.click(function(){
|
||||
nh_reset_form(params);
|
||||
toggleAndSettingWordsVal(params.div_form, params.textarea);
|
||||
});
|
||||
params.submit_btn.click(function(){
|
||||
params.form.submit();
|
||||
});
|
||||
params.textarea.data('init',1);
|
||||
}
|
||||
params.cancel_btn.click();
|
||||
setTimeout(function(){
|
||||
if(!params.div_form.is(':hidden')){
|
||||
params.textarea.show();
|
||||
params.textarea.focus();
|
||||
params.textarea.hide();
|
||||
}
|
||||
},300);
|
||||
});
|
||||
|
||||
$("div[nhname='new_message']").each(function(){
|
||||
var params = {};
|
||||
params.kindutil = K;
|
||||
params.div_form = $(this);
|
||||
params.form = $("form",params.div_form);
|
||||
if(params.form==undefined || params.form.length==0){
|
||||
return;
|
||||
}
|
||||
params.textarea = $("textarea[nhname='new_message_textarea']",params.div_form);
|
||||
params.contentmsg = $("p[nhname='contentmsg']",params.div_form);
|
||||
params.toolbar_container = $("div[nhname='toolbar_container']",params.div_form);
|
||||
params.cancel_btn = $("#new_message_cancel_btn");
|
||||
params.submit_btn = $("#new_message_submit_btn");
|
||||
|
||||
if(params.textarea.data('init') == undefined){
|
||||
params.editor = init_editor(params);
|
||||
init_form(params);
|
||||
params.cancel_btn.click(function(){
|
||||
nh_reset_form(params);
|
||||
});
|
||||
params.submit_btn.click(function(){
|
||||
params.form.submit();
|
||||
});
|
||||
params.textarea.data('init',1);
|
||||
$(this).show();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//编辑个人简介
|
||||
function show_edit_user_introduction() {
|
||||
$("#user_brief_introduction_show").hide();
|
||||
$("#user_brief_introduction_edit").show();
|
||||
$("#user_brief_introduction_edit").focus();
|
||||
}
|
||||
|
||||
//编辑个人简介完成之后提交
|
||||
function edit_user_introduction(url){
|
||||
$.get(
|
||||
url,
|
||||
{ brief_introduction: $("#user_brief_introduction_edit").val() },
|
||||
function (data) {
|
||||
|
||||
function init_list_more_div(params){
|
||||
var p=params;
|
||||
p.exbtn.click(function(){
|
||||
var isclose = p.container.data('isclose');
|
||||
var hasmore = p.container.data('hasmore');
|
||||
if(isclose == '1'){
|
||||
$("div[nhname='rec']",p.container).show();
|
||||
p.container.data('isclose','0');
|
||||
change_status_4_list_more_div(params);
|
||||
return;
|
||||
}
|
||||
);
|
||||
if(hasmore == '0'){
|
||||
change_status_4_list_more_div(params,'get');
|
||||
return;
|
||||
}
|
||||
var url = p.container.data('url');
|
||||
if($("div[nhname='rec']",p.container).length > 0){
|
||||
var lastid = $("div[nhname='rec']",p.container).filter(':last').data('id');
|
||||
url += "?lastid="+lastid;
|
||||
var lasttime = $("div[nhname='rec']",p.container).filter(':last').data('time');
|
||||
if(lasttime != undefined){
|
||||
url += "&lasttime="+lasttime;
|
||||
}
|
||||
}
|
||||
$.ajax( {url:url,dataType:'text',success:function(data){
|
||||
var html = $("<div>"+data+"</div>");
|
||||
var lens = $("div[nhname='rec']",html).length;
|
||||
if(lens < p.size){
|
||||
p.container.data('hasmore','0');
|
||||
}
|
||||
if(lens>0){
|
||||
var currpage = parseInt(p.container.data('currpage'))+1;
|
||||
p.container.data('currpage',currpage);
|
||||
p.container.append(html.html())
|
||||
}
|
||||
change_status_4_list_more_div(params,'get');
|
||||
p.div.show();
|
||||
}} );
|
||||
});
|
||||
p.clbtn.click(function(){
|
||||
var i=0;
|
||||
$("div[nhname='rec']",p.container).each(function(){
|
||||
i++;
|
||||
if(i> p.size){
|
||||
$(this).hide();
|
||||
}
|
||||
});
|
||||
p.container.data('isclose','1');
|
||||
change_status_4_list_more_div(params);
|
||||
});
|
||||
p.exbtn.click();
|
||||
}
|
||||
function change_status_4_list_more_div(params,opt){
|
||||
var p=params;
|
||||
if($("div[nhname='rec']",p.container).length == 0 && opt != 'get'){
|
||||
p.exbtn.click();
|
||||
return;
|
||||
}
|
||||
var show_lens = $("div[nhname='rec']",p.container).length - $("div[nhname='rec']",p.container).filter(':hidden').length;
|
||||
if( show_lens > p.size ){
|
||||
p.clbtn.show();
|
||||
}else{
|
||||
p.clbtn.hide();
|
||||
}
|
||||
if($("div[nhname='rec']",p.container).length == 0){
|
||||
p.exbtn.html(p.nodatamsg);
|
||||
}else if( p.container.data('hasmore') == '1' || p.container.data('isclose')=='1' ){
|
||||
p.exbtn.html('点击展开更多');
|
||||
}else{
|
||||
p.exbtn.html('没有更多了');
|
||||
}
|
||||
}
|
||||
function init_list_more_div_params(div){
|
||||
var params = {};
|
||||
params.div = div;
|
||||
params.container = $("div[nhname='container']",div);
|
||||
params.exbtn = $("a[nhname='expand']",div);
|
||||
params.clbtn = $("a[nhname='close']",div);
|
||||
params.size = params.container.data('pagesize');
|
||||
params.nodatamsg = params.container.data('nodatamsg');
|
||||
if( params.size == undefined ){
|
||||
params.size = 13;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
$(function(){
|
||||
$("div[nhname='list_more_div']").each(function(){
|
||||
var params = init_list_more_div_params($(this));
|
||||
init_list_more_div(params)
|
||||
});
|
||||
});
|
||||
|
||||
$(function(){
|
||||
$(".newsType").mouseover(function(){
|
||||
|
|
Loading…
Reference in New Issue