shagit 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +29 -0
- data/LICENSE +21 -0
- data/README.md +58 -0
- data/VERSION.yml +5 -0
- data/bin/shagit +11 -0
- data/config.ru +3 -0
- data/config.yml +6 -0
- data/lib/authorization.rb +33 -0
- data/lib/config_info.rb +9 -0
- data/lib/enhancing_grit.rb +29 -0
- data/lib/helpers/helpers.rb +115 -0
- data/lib/shagit.rb +60 -0
- data/public/buttons.css +82 -0
- data/public/reset.css +7 -0
- data/public/shagit.css +354 -0
- data/rakefile +99 -0
- data/shagit_app.rb +137 -0
- data/test/acceptance/acceptance_test.rb +86 -0
- data/test/unit/shagit_test.rb +49 -0
- data/views/delete.haml +13 -0
- data/views/delete_confirmation.haml +6 -0
- data/views/index.haml +14 -0
- data/views/layout.haml +17 -0
- data/views/login.haml +15 -0
- data/views/login_failed.haml +15 -0
- data/views/logout.haml +9 -0
- data/views/new.haml +10 -0
- data/views/new_confirmation.haml +6 -0
- data/views/repo.haml +41 -0
- metadata +160 -0
data/CHANGES
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
0.3 (2010-03-17)
|
2
|
+
==================================
|
3
|
+
* refactored global variables into a singleton class
|
4
|
+
* changed require statement inside config.ru to reflect recent name changes
|
5
|
+
* modified stylesheet
|
6
|
+
* exchanged OS call 'hostname' with request.env
|
7
|
+
* fixed repository location bug
|
8
|
+
* added rakefile
|
9
|
+
* added proper dependencies for production & development to gem
|
10
|
+
* added new test case to unit test (tests loading of the configuration file)
|
11
|
+
* added gem building using jeweler
|
12
|
+
* added runtime checking (to check if Shagit was installed using the gem)
|
13
|
+
|
14
|
+
0.2 (2010-02-27)
|
15
|
+
==================================
|
16
|
+
* fixed password input field
|
17
|
+
* implemented working_dir setting
|
18
|
+
* converted README to Markdown format
|
19
|
+
* moved helpers folder to lib
|
20
|
+
* added $LOAD_PATH statements
|
21
|
+
* renamed shagit.rb to shagit_app.rb due to name conflicts with lib/shagit.rb
|
22
|
+
* added acceptance tests using Webrat
|
23
|
+
|
24
|
+
0.1 Initial Release (2010-01-11)
|
25
|
+
==================================
|
26
|
+
* Basic create, optimize and delete functions implemented for repositories
|
27
|
+
* Authentication system implemented
|
28
|
+
* Integration of Integrity's CSS and layout
|
29
|
+
* Deployment via Phusion Passenger
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2009, 2010 Martin Gajdos
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
Shagit
|
2
|
+
============
|
3
|
+
|
4
|
+
Description: A simple web application for local, private git repository management written in Sinatra
|
5
|
+
Copyright (c) 2009, 2010 Martin Gajdos
|
6
|
+
|
7
|
+
released under the MIT license
|
8
|
+
|
9
|
+
Introduction
|
10
|
+
------------
|
11
|
+
|
12
|
+
Shagit is a simple web application for managing your local git repositories. There's no need for a database and repositories are being privately published using ssh.
|
13
|
+
|
14
|
+
Dependencies
|
15
|
+
------------
|
16
|
+
|
17
|
+
* Sinatra
|
18
|
+
* Haml
|
19
|
+
* Grit
|
20
|
+
* Rack-Test
|
21
|
+
* Webrat
|
22
|
+
|
23
|
+
Installation
|
24
|
+
------------
|
25
|
+
|
26
|
+
You will need a working installation of Ruby >=1.8.6, RubyGems >= 1.3.5 and Git >= 1.5
|
27
|
+
|
28
|
+
Clone the repository:
|
29
|
+
|
30
|
+
git clone git://github.com/unabatede/shagit.git
|
31
|
+
|
32
|
+
Install the Sinatra, Haml & Grit gems:
|
33
|
+
|
34
|
+
gem install sinatra haml grit rack-test webrat
|
35
|
+
|
36
|
+
Run it (you should consider other options for production deployment):
|
37
|
+
|
38
|
+
cd shagit
|
39
|
+
ruby shagit.rb -e production
|
40
|
+
|
41
|
+
Deployment
|
42
|
+
----------
|
43
|
+
|
44
|
+
Using Apache & Phusion Passenger and the following to a new virtual host file (adjust to suite your needs):
|
45
|
+
|
46
|
+
<VirtualHost *:80>
|
47
|
+
DocumentRoot /var/www/shagit/public
|
48
|
+
<Directory /var/www/shagit/public>
|
49
|
+
Allow from all
|
50
|
+
Options -MultiViews
|
51
|
+
</Directory>
|
52
|
+
</VirtualHost>
|
53
|
+
|
54
|
+
|
55
|
+
Also, inside Shagit's folder, create a 'tmp' and 'public' folder:
|
56
|
+
|
57
|
+
cd /var/www/shagit
|
58
|
+
mkdir tmp; mkdir public
|
data/bin/shagit
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..')
|
3
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'sinatra'
|
7
|
+
require 'shagit_app'
|
8
|
+
|
9
|
+
set :environment, :production
|
10
|
+
|
11
|
+
Sinatra::Application.run!
|
data/config.ru
ADDED
data/config.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Authorization
|
3
|
+
|
4
|
+
def authorize!(username, password)
|
5
|
+
# get the only instance of the Config class
|
6
|
+
config_data = ConfigInfo.instance
|
7
|
+
|
8
|
+
if (username == config_data.username) && (password == config_data.password)
|
9
|
+
session[:authorized] = true
|
10
|
+
true
|
11
|
+
else
|
12
|
+
session[:authorized] = false
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_authorized?
|
19
|
+
if session[:authorized]
|
20
|
+
true
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def requires_login!
|
27
|
+
if !is_authorized?
|
28
|
+
redirect '/login'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
data/lib/config_info.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# this adds a method called 'repo_name' that returns only the filename without path or file extension
|
2
|
+
require 'pathname'
|
3
|
+
require 'find'
|
4
|
+
|
5
|
+
module Grit
|
6
|
+
# return foldername including extension
|
7
|
+
def shagit_foldername
|
8
|
+
foldername = Pathname.new(self.path).basename.to_s
|
9
|
+
end
|
10
|
+
|
11
|
+
# return foldername from path without extension
|
12
|
+
def shagit_name
|
13
|
+
foldername = Pathname.new(self.shagit_foldername).basename.to_s
|
14
|
+
|
15
|
+
# extract repository name without .git extension
|
16
|
+
repo_name = foldername.match(/^[\w\s]+/).to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
# return size of folder containing the repository in Kilobytes
|
20
|
+
def shagit_size
|
21
|
+
dirsize = 0
|
22
|
+
# recursively search the repositories path and sum up all file sizes
|
23
|
+
Find.find(self.path) do |file|
|
24
|
+
dirsize += File.stat(file).size
|
25
|
+
end
|
26
|
+
|
27
|
+
dirsize
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "../"))
|
2
|
+
|
3
|
+
require 'authorization'
|
4
|
+
require 'pathname'
|
5
|
+
require 'config_info'
|
6
|
+
|
7
|
+
include Sinatra::Authorization
|
8
|
+
|
9
|
+
def stylesheets(*sheets)
|
10
|
+
sheets.each { |sheet|
|
11
|
+
haml_tag(:link, :href => "/#{sheet}.css", :type => "text/css", :rel => "stylesheet")
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def cycle(*values)
|
16
|
+
@cycles ||= {}
|
17
|
+
@cycles[values] ||= -1 # first value returned is 0
|
18
|
+
next_value = @cycles[values] = (@cycles[values] + 1) % values.size
|
19
|
+
values[next_value]
|
20
|
+
end
|
21
|
+
|
22
|
+
def full_path
|
23
|
+
# get the only instance of the Config class
|
24
|
+
config_data = ConfigInfo.instance
|
25
|
+
#hostname = `hostname`
|
26
|
+
hostname = request.env['SERVER_NAME']
|
27
|
+
if (config_data.working_dir == '.')
|
28
|
+
full_path = "/#{@current_repo.shagit_foldername}"
|
29
|
+
else
|
30
|
+
full_path = "#{config_data.working_dir}/#{@current_repo.shagit_foldername}"
|
31
|
+
end
|
32
|
+
|
33
|
+
"ssh://#{reformat_string(hostname)}#{reformat_string(full_path)}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def reformat_string(source)
|
37
|
+
source.strip
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_config_file_dir(dir)
|
41
|
+
# check whether this is being called from a webrat test and if yes, adjust the calling path for the config_data file
|
42
|
+
if dir[/[\w]+$/] == "helpers"
|
43
|
+
dir = Pathname.new(File.expand_path(File.dirname(__FILE__)))
|
44
|
+
dir.parent.parent
|
45
|
+
else
|
46
|
+
dir
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def load_config(file)
|
51
|
+
current_dir = File.expand_path(File.dirname(__FILE__))
|
52
|
+
absolute_filepath = File.join(get_config_file_dir(current_dir), file)
|
53
|
+
|
54
|
+
if File.exist?(absolute_filepath)
|
55
|
+
config_data = ConfigInfo.instance
|
56
|
+
|
57
|
+
yaml = YAML.load_file(absolute_filepath)
|
58
|
+
config_data.username = yaml['username']
|
59
|
+
config_data.password = yaml['password']
|
60
|
+
|
61
|
+
working_dir = yaml['working_dir']
|
62
|
+
|
63
|
+
# check whether working_dir has been specified
|
64
|
+
unless (working_dir == nil) || (working_dir.empty?)
|
65
|
+
working_dir = working_dir.strip
|
66
|
+
# check whether the specified folder exists, if not, set it to the directory Shagit's located at
|
67
|
+
if FileTest.directory?(working_dir)
|
68
|
+
config_data.working_dir = working_dir
|
69
|
+
else
|
70
|
+
set_working_dir_to_current_dir
|
71
|
+
end
|
72
|
+
else
|
73
|
+
set_working_dir_to_current_dir
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_working_dir_to_current_dir
|
79
|
+
config_data = ConfigInfo.instance
|
80
|
+
config_data.working_dir = "."
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_fullpath(path)
|
84
|
+
config_data = ConfigInfo.instance
|
85
|
+
|
86
|
+
# add the .git extension if it isn't there already
|
87
|
+
if !path.include?('.git')
|
88
|
+
path = "#{path}.git"
|
89
|
+
end
|
90
|
+
|
91
|
+
fullpath = "#{config_data.working_dir}/#{path}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def check_if_started_from_gem(current_working_directory)
|
95
|
+
config_data = ConfigInfo.instance
|
96
|
+
|
97
|
+
# check if the bin folder is included in the directory this file is located in
|
98
|
+
if current_working_directory.include?('bin')
|
99
|
+
if (config_data.working_dir == '.')
|
100
|
+
puts "FATAL ERROR: The directory for saving repositories cannot be used!"
|
101
|
+
puts "Please set the path in your config.yml accordingly."
|
102
|
+
exit
|
103
|
+
end
|
104
|
+
|
105
|
+
# if so, we know Shagit has been installed via gem and the application's root folder has to be redirected
|
106
|
+
set :root, current_working_directory
|
107
|
+
end
|
108
|
+
|
109
|
+
# check if the working directory for saving repositories is writable
|
110
|
+
if !FileTest.writable_real?(config_data.working_dir)
|
111
|
+
puts "FATAL ERROR: The directory for saving repositories (#{config_data.working_dir}) is not writable!"
|
112
|
+
puts "Please adjust your config.yml accordingly."
|
113
|
+
exit
|
114
|
+
end
|
115
|
+
end
|
data/lib/shagit.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'grit'
|
4
|
+
require 'config_info'
|
5
|
+
include Grit
|
6
|
+
|
7
|
+
# Main class for shagit, initializes and manages all repositories
|
8
|
+
class Shagit
|
9
|
+
attr_reader :repositories
|
10
|
+
|
11
|
+
# checks if a specified directory is a github repository
|
12
|
+
def self.is_it_a_git_repo?(path)
|
13
|
+
if FileTest.directory?(path) && FileTest.directory?("#{path}/hooks") && FileTest.directory?("#{path}/info") && FileTest.directory?("#{path}/objects") && FileTest.directory?("#{path}/refs")
|
14
|
+
true
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# initialize shagit by looking for git repositories in specified path
|
21
|
+
def initialize
|
22
|
+
# get the only instance of the Config class
|
23
|
+
config_data = ConfigInfo.instance
|
24
|
+
|
25
|
+
@repositories = Array.new
|
26
|
+
Dir.foreach(config_data.working_dir) do |path|
|
27
|
+
fullpath = "#{config_data.working_dir}/#{path}"
|
28
|
+
|
29
|
+
if Shagit.is_it_a_git_repo?(fullpath)
|
30
|
+
puts "adding repository: #{fullpath}"
|
31
|
+
# create a new Grit repository object if a directory has been found that looks to be a folder containing a git repository
|
32
|
+
@repositories << Repo.new(fullpath) unless (path == '.git')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# creates a new bare repository for the specified path if it doesn't already exist
|
38
|
+
def self.create_repo(full_repo_name)
|
39
|
+
# if the repository already exists, simply return 'false'
|
40
|
+
if FileTest.directory?(full_repo_name)
|
41
|
+
false
|
42
|
+
else
|
43
|
+
puts "creating bare repository at: #{full_repo_name}"
|
44
|
+
#new_repo = Grit::Repo.init_bare(full_repo_name)
|
45
|
+
puts "pwd: #{Dir.pwd}"
|
46
|
+
new_repo = Grit::Repo.init_bare(full_repo_name)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# deletes an existing repository
|
51
|
+
def self.delete_repo!(full_repo_name)
|
52
|
+
if FileTest.directory?(full_repo_name)
|
53
|
+
FileUtils.rm_rf(full_repo_name)
|
54
|
+
true
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/public/buttons.css
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
/* --------------------------------------------------------------
|
2
|
+
|
3
|
+
buttons.css
|
4
|
+
* Gives you some great CSS-only buttons.
|
5
|
+
|
6
|
+
Created by Kevin Hale [particletree.com]
|
7
|
+
* particletree.com/features/rediscovering-the-button-element
|
8
|
+
|
9
|
+
See Readme.txt in this folder for instructions.
|
10
|
+
|
11
|
+
-------------------------------------------------------------- */
|
12
|
+
|
13
|
+
button {
|
14
|
+
display:block;
|
15
|
+
float:left;
|
16
|
+
margin:0 0.583em 0.667em 0;
|
17
|
+
padding:5px 10px 5px 7px; /* Links */
|
18
|
+
|
19
|
+
border:1px solid #dedede;
|
20
|
+
border-top:1px solid #eee;
|
21
|
+
border-left:1px solid #eee;
|
22
|
+
|
23
|
+
background-color:#f5f5f5;
|
24
|
+
font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif;
|
25
|
+
font-size:100%;
|
26
|
+
line-height:130%;
|
27
|
+
text-decoration:none;
|
28
|
+
font-weight:bold;
|
29
|
+
color:#565656;
|
30
|
+
cursor:pointer;
|
31
|
+
}
|
32
|
+
button {
|
33
|
+
width:auto;
|
34
|
+
overflow:visible;
|
35
|
+
padding:4px 10px 3px 7px; /* IE6 */
|
36
|
+
}
|
37
|
+
button[type] {
|
38
|
+
padding:4px 10px 4px 7px; /* Firefox */
|
39
|
+
line-height:17px; /* Safari */
|
40
|
+
}
|
41
|
+
*:first-child+html button[type] {
|
42
|
+
padding:4px 10px 3px 7px; /* IE7 */
|
43
|
+
}
|
44
|
+
button img {
|
45
|
+
margin:0 3px -3px 0 !important;
|
46
|
+
padding:0;
|
47
|
+
border:none;
|
48
|
+
width:16px;
|
49
|
+
height:16px;
|
50
|
+
float:none;
|
51
|
+
}
|
52
|
+
|
53
|
+
|
54
|
+
/* Button colors
|
55
|
+
-------------------------------------------------------------- */
|
56
|
+
|
57
|
+
/* Standard */
|
58
|
+
button:hover {
|
59
|
+
background-color:#dff4ff;
|
60
|
+
border:1px solid #c2e1ef;
|
61
|
+
color:#336699;
|
62
|
+
}
|
63
|
+
|
64
|
+
/* Positive */
|
65
|
+
body .positive {
|
66
|
+
color:#529214;
|
67
|
+
}
|
68
|
+
button.positive:hover {
|
69
|
+
background-color:#E6EFC2;
|
70
|
+
border:1px solid #C6D880;
|
71
|
+
color:#529214;
|
72
|
+
}
|
73
|
+
|
74
|
+
/* Negative */
|
75
|
+
body .negative {
|
76
|
+
color:#d12f19;
|
77
|
+
}
|
78
|
+
button.negative:hover {
|
79
|
+
background:#fbe3e4;
|
80
|
+
border:1px solid #fbc2c4;
|
81
|
+
color:#d12f19;
|
82
|
+
}
|