hubbard 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ /data
data/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2010 Matthew Foemmel
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ * The names of the contributors may not be used to endorse or promote
15
+ products derived from this software without specific prior written
16
+ permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
22
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,61 @@
1
+ Description
2
+ ===========
3
+
4
+ Hubbard is a command line tool for managing shared git repositories in a team environment. It provides a security model so that users can easily control who has access to the projects they create.
5
+
6
+ Hubbard uses public SSH keys to keep track of who is executing what commands. This means you only have to create a single account on the server, instead of one per user.
7
+
8
+ Hubbards was heavily inspired by gitosis, another tool for managing git repositories. However, the goal of Hubbard was to place less burden on the system administrator by allowing users to manage permissions for their own projects.
9
+
10
+ How It Works
11
+ ============
12
+
13
+ All comminication between users the the Hubbard server happens over SSH. Users must register their public SSH keys with the server before they can connect to it.
14
+
15
+ When a user connects to the Hubbard server, the SSH daemon tries to find the user's public SSH key the "~/.ssh/authorized_keys" file on the server. That file also contains information about which user to associate with that SSH key. That information is automatically passed to the "hubbard" executable, so there is no way for users to run other programs on the server.
16
+
17
+ Installation
18
+ ===========
19
+
20
+ ### Server ###
21
+
22
+ The first step is to create a user account called "hub" on the server machine and log into that account. You'll also need to make sure that Ruby and Rubygems are installed on the machine. You can then install hubbard like this:
23
+
24
+ $ gem install hubbard
25
+
26
+ Unless you installed the gem using "sudo", the "hubbard" executable will be found somewhere under the "~/.gem" directory. You'll need to make sure this directory is included in the PATH whenever anyone uses SSH to connect. You can do this by adding the following line to the top of your "~/.bashrc" file (make sure the path matches):
27
+
28
+ export PATH=$PATH:~/.gem/ruby/1.8/bin
29
+
30
+ The next step is to create an SSH keypair to access the "admin" account on the Hubbard server. You should only use this key when performing tasks that require admin access. Run this on the machine that you'll be accessing Hubbard from (i.e. your local workstation, not the server):
31
+
32
+ $ ssh-keygen -f ~/.ssh/hubadmin
33
+
34
+ Now we'll need to copy the public key up to the server. Assuming that the hubbard server is named "example" :
35
+
36
+ $ cat ~/.ssh/hubadmin.pub | ssh hub@example hubbard admin add-key default
37
+
38
+ On your local workstation, you can now create an alias for executing admin commands:
39
+
40
+ $ alias hubadmin='ssh -i ~/.ssh/hubadmin hub@example.com'
41
+
42
+ Test the configuration:
43
+
44
+ $ hubadmin list-users
45
+ admin
46
+
47
+ You'll probably want to create a normal (i.e. non-admin) account as described in the next section.
48
+
49
+ ### Client ###
50
+
51
+ There is no executable to install on the client, but you will have to generate an SSH keypair and send the public key (i.e. the one that ends with ".pub") to your administrator. Your administrator will have to run the following command:
52
+
53
+ $ cat <keyfile> | hubadmin run-as <username> add-key default
54
+
55
+ Assuming your SSH keys have been set up correcly, you can simply SSH into the server machine to executable commands. You'll probably want to set up an alias to make this easier, however. For example, if your server is running at "example", you can do this:
56
+
57
+ $ alias hub='ssh hub@example'
58
+
59
+ To test it, run:
60
+
61
+ $ hub help
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "hubbard"
8
+ gem.summary = %Q{Hubbard is a command line tool for managing git repositories.}
9
+ gem.description = %Q{Hubbard is a command line tool for managing git repositories.}
10
+ gem.email = "git@foemmel.com"
11
+ gem.homepage = "http://github.com/mfoemmel/hubbard"
12
+ gem.authors = ["Matthew Foemmel"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.files << Dir['commands/*.rb']
15
+ gem.bindir = 'bin'
16
+ gem.executables << 'hubbard'
17
+ gem.require_path = ''
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
22
+ end
23
+
24
+ require 'spec/rake/spectask'
25
+ Spec::Rake::SpecTask.new(:spec) do |spec|
26
+ spec.libs << 'lib' << 'spec'
27
+ spec.spec_files = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
31
+ spec.libs << 'lib' << 'spec'
32
+ spec.pattern = 'spec/**/*_spec.rb'
33
+ spec.rcov = true
34
+ end
35
+
36
+ task :spec => :check_dependencies
37
+
38
+ task :default => :spec
39
+
40
+ require 'rake/rdoctask'
41
+ Rake::RDocTask.new do |rdoc|
42
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
43
+
44
+ rdoc.rdoc_dir = 'rdoc'
45
+ rdoc.title = "hubbard #{version}"
46
+ rdoc.rdoc_files.include('README*')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+
5
+ PROJECT_REGEX='[a-zA-Z0-9\-]{1,32}'
6
+ REPOSITORY_REGEX='[a-zA-Z0-9\-]{1,32}'
7
+ USERNAME_REGEX='[a-zA-Z0-9\-]{1,32}'
8
+
9
+ HUB_DATA=ENV['HUB_DATA'] || File.expand_path("~/.hubbard")
10
+
11
+ FileUtils.mkdir_p(File.join(HUB_DATA, "projects"))
12
+ FileUtils.mkdir_p(File.join(HUB_DATA, "accounts"))
13
+
14
+ def next_arg(msg)
15
+ if ARGV.length < 1
16
+ $stderr.puts msg
17
+ exit 1
18
+ end
19
+ ARGV.shift
20
+ end
21
+
22
+ def check_status(msg)
23
+ if $!.exitstatus != 0
24
+ $sderr.puts msg
25
+ exit 1
26
+ end
27
+ end
28
+
29
+ def validate_project_name(name)
30
+ if name !~ /#{PROJECT_REGEX}/
31
+ $stderr.put "Project names can only contain letter, numbers, and hyphens"
32
+ exit 1
33
+ end
34
+ end
35
+
36
+ def validate_repository_name(name)
37
+ if name !~ /#{REPOSITORY_REGEX}/
38
+ $stderr.put "Repository names can only contain letter, numbers, and hyphens"
39
+ exit 1
40
+ end
41
+ end
42
+
43
+ def validate_user_name(name)
44
+ if name !~ /#{USERNAME_REGEX}/
45
+ $stderr.put "User names can only contain letter, numbers, and hyphens"
46
+ exit 1
47
+ end
48
+ end
49
+
50
+ def validate_action_name(name)
51
+ unless name == 'read' || name == 'write' || name == 'admin'
52
+ $stderr.put "Not a valid action (must be one of: read, write, admin)"
53
+ exit 1
54
+ end
55
+ end
56
+
57
+ username = next_arg "Please specify the username to run as"
58
+ command = next_arg "Please specify a command to run"
59
+
60
+ if command == "run-as"
61
+ if username != "admin"
62
+ $stderr.puts "You don't have permission to do that"
63
+ exit 1
64
+ end
65
+ username = next_arg "Please specify the username to run as"
66
+ command = next_arg "Please specify a command to run"
67
+ end
68
+
69
+ @username = username
70
+
71
+ def implies(a1, a2)
72
+ case a1
73
+ when 'admin'
74
+ true
75
+ when 'write'
76
+ a2 != 'admin'
77
+ when 'read'
78
+ a2 == 'read'
79
+ else
80
+ raise "Unknown action type: *#{a1}*"
81
+ end
82
+ end
83
+
84
+ def authorize(project_name, action)
85
+ unless is_authorized(project_name, action)
86
+ $stderr.puts "You don't have permission to do that"
87
+ exit 3
88
+ end
89
+ end
90
+
91
+ def is_authorized(project_name, action)
92
+ Dir.chdir(find_project_dir(project_name)) do
93
+ if action == 'read' && File.read('.visibility').strip == 'public'
94
+ return true
95
+ end
96
+ File.read(".permissions").split("\n").each do |line|
97
+ permission = line.strip.split('=')
98
+ line_username = permission[0]
99
+ line_action = permission[1]
100
+ if line_username == @username && implies(line_action, action)
101
+ return true
102
+ end
103
+ end
104
+ false
105
+ end
106
+ end
107
+
108
+ def find_account_dir(user_name)
109
+ File.join(HUB_DATA, "accounts", user_name)
110
+ end
111
+
112
+ def find_project_dir(project_name)
113
+ File.join(HUB_DATA, "projects", project_name)
114
+ end
115
+
116
+ def find_repository_dir(project_name, repository_dir)
117
+ File.join(find_project_dir(project_name), repository_dir)
118
+ end
119
+
120
+ def read_project_name
121
+ project_name = next_arg("Please specify a project name")
122
+ validate_project_name(project_name)
123
+ project_name
124
+ end
125
+
126
+ def read_repository_name
127
+ repository_name = next_arg("Please specify a repository name")
128
+ validate_repository_name(repository_name)
129
+ repository_name
130
+ end
131
+
132
+ def read_user_name
133
+ user_name = next_arg("Please specify a username")
134
+ validate_user_name(user_name)
135
+ user_name
136
+ end
137
+
138
+ def sync_keys
139
+ File.open(File.expand_path("~/.ssh/authorized_keys"), "w") do |file|
140
+ Dir.entries(File.join(HUB_DATA, "accounts")).each do |account|
141
+ next if account == '.' || account == '..'
142
+ key_dir = File.join(HUB_DATA, "accounts", account, "keys")
143
+ Dir.entries(key_dir).each do |name|
144
+ next if name == '.' || name == '..'
145
+ key = File.read(File.join(key_dir, name))
146
+ file << "command=\"hubbard #{@username}\" #{key} #{name}\n"
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ command_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "commands", "#{command}.rb"))
153
+
154
+ if File.exist?(command_file)
155
+ load command_file
156
+ else
157
+ $stderr.puts "Unknown command: #{command}"
158
+ exit 1
159
+ end
@@ -0,0 +1,23 @@
1
+ name = next_arg("Please specify the key name")
2
+ if name !~ /[a-zA-Z0-9]+/
3
+ $stderr.puts "Not a valid key name (letters and numbers only)"
4
+ exit 1
5
+ end
6
+
7
+ key = $stdin.read.strip
8
+ if key !~ /(ssh-rsa|ssh-dsa) ([a-zA-Z0-9\+\/]+[=]*)/
9
+ $stderr.puts "Not a valid key"
10
+ exit 1
11
+ end
12
+
13
+ type = $1
14
+ value = $2
15
+
16
+ dirname = File.join(find_account_dir(@username), "keys")
17
+ FileUtils.mkdir_p(dirname)
18
+ filename = File.join(dirname, name)
19
+ File.open(filename, "w") do |file|
20
+ file << type << " " << value
21
+ end
22
+
23
+ sync_keys
@@ -0,0 +1,23 @@
1
+ project_name = read_project_name
2
+ authorize(project_name, 'admin')
3
+ dir = find_project_dir(project_name)
4
+ username = ARGV.shift
5
+ action = ARGV.shift
6
+ unless ['admin','write','read'].member?(action)
7
+ $stderr.puts "Not a valid action (must be one of: read, write, admin)"
8
+ exit 1
9
+ end
10
+
11
+ File.open(File.join(dir, ".lock"), "w+") do |lock|
12
+ lock.flock(File::LOCK_EX)
13
+ begin
14
+ filename = File.join(dir, ".permissions")
15
+ permissions = File.read(filename).split("\n").map { |line| line.strip }.select { |line| line.split('=')[0] != username }
16
+ permissions << "#{username}=#{action}"
17
+ File.open(filename, "w") do |file|
18
+ permissions.each { |permission| file << permission << "\n" }
19
+ end
20
+ ensure
21
+ lock.flock(File::LOCK_UN)
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ require 'fileutils'
2
+
3
+ project_name = read_project_name
4
+ dir = find_project_dir(project_name)
5
+ if File.exist?(dir)
6
+ $stderr.puts "Project already exists with that name"
7
+ exit 4
8
+ end
9
+ unless Dir.mkdir(dir)
10
+ $stderr.puts "Unable to create directory: #{dir}"
11
+ end
12
+ visibility = ARGV.member?("--private") ? "private" : "public"
13
+ File.open(File.join(dir, ".permissions"), "w") { |f| f << "#{@username}=admin\n" }
14
+ File.open(File.join(dir, ".visibility"), "w") { |f| f << "#{visibility}\n" }
@@ -0,0 +1,8 @@
1
+ project_name = read_project_name
2
+ repository_name = read_repository_name
3
+ authorize(project_name, 'admin')
4
+ dir = find_repository_dir(project_name, repository_name)
5
+ FileUtils.mkdir_p(dir)
6
+ Dir.chdir(dir) do
7
+ exec "git init --bare"
8
+ end
@@ -0,0 +1,4 @@
1
+ project_name = read_project_name
2
+ dir = find_project_dir(project_name)
3
+ authorize(project_name, 'admin')
4
+ FileUtils.rm_rf(dir)
@@ -0,0 +1,9 @@
1
+ unless ARGV.shift =~ /(#{PROJECT_REGEX})\/(#{REPOSITORY_REGEX}).git/
2
+ $stderr.puts "Repository not found"
3
+ exit 1
4
+ end
5
+ project_name = $1
6
+ repository_name = $2
7
+ authorize(project_name, 'write')
8
+ dir = find_repository_dir(project_name, repository_name)
9
+ exec "git-receive-pack #{dir}"
@@ -0,0 +1,9 @@
1
+ unless ARGV.shift =~ /(#{PROJECT_REGEX})\/(#{REPOSITORY_REGEX}).git/
2
+ $stderr.puts "Repository not found"
3
+ exit 1
4
+ end
5
+ project_name = $1
6
+ repository_name = $2
7
+ authorize(project_name, 'read')
8
+ dir = find_repository_dir(project_name, repository_name)
9
+ exec "git-upload-pack #{dir}"
@@ -0,0 +1,21 @@
1
+ $stderr.puts <<-END
2
+ Usage: hub <command>
3
+
4
+ Projects:
5
+
6
+ list-projects
7
+ create-project <project>
8
+ delete-project <project>
9
+
10
+ Repositories:
11
+
12
+ create-repository <project> <repository>
13
+ delete-repository <project> <repository>
14
+
15
+ Permissions:
16
+
17
+ add-permission <project> <username> read|write|admin
18
+ remove-permission <project> <username>
19
+
20
+ END
21
+ exit 0
@@ -0,0 +1,5 @@
1
+ dirname = File.join(find_account_dir(@username), "keys")
2
+ Dir.entries(dirname).each do |name|
3
+ next if name == '.' || name == '..'
4
+ puts name
5
+ end
@@ -0,0 +1,15 @@
1
+ project_name = read_project_name
2
+ authorize(project_name, 'admin')
3
+ dir = find_project_dir(project_name)
4
+ username = ARGV.shift
5
+ action = ARGV.shift
6
+ unless ['admin','write','read'].member?(action)
7
+ $stderr.puts "Not a valid action (must be one of: read, write, admin)"
8
+ exit 1
9
+ end
10
+ File.open(File.join(dir, ".permissions"), "r+") do |f|
11
+ f.flock(File::LOCK_EX)
12
+ contents = f.read
13
+ puts contents
14
+ f.flock(File::LOCK_UN)
15
+ end
@@ -0,0 +1,6 @@
1
+ Dir.foreach(File.join(HUB_DATA, 'projects')) do |dir|
2
+ next if dir == "." || dir == ".."
3
+ if is_authorized(dir, 'read')
4
+ puts dir
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ project_name = read_project_name
2
+ authorize(project_name, 'read')
3
+ Dir.foreach(find_project_dir(project_name)) do |repository_name|
4
+ next if repository_name =~ /^\./
5
+ git_url = "#{ENV['USER']}@#{ENV['HUB_HOST']}:#{project_name}/#{repository_name}.git"
6
+ puts "#{repository_name}\t#{git_url}"
7
+ end
@@ -0,0 +1,21 @@
1
+ name = next_arg("Please specify the key name")
2
+ if name !~ /[a-zA-Z0-9]+/
3
+ $stderr.puts "Not a valid key name (letters and numbers only)"
4
+ exit 1
5
+ end
6
+
7
+ dirname = File.join(find_account_dir(@username), "keys")
8
+ FileUtils.mkdir_p(dirname)
9
+
10
+ filename = File.join(dirname, name)
11
+ if !File.exist?(filename)
12
+ $stderr.puts "Key not found"
13
+ exit 1
14
+ end
15
+
16
+ unless FileUtils.rm(filename)
17
+ $stderr.puts "Unable to delete key"
18
+ exit 1
19
+ end
20
+
21
+ sync_keys
@@ -0,0 +1,16 @@
1
+ project_name = read_project_name
2
+ authorize(project_name, 'admin')
3
+ dir = find_project_dir(project_name)
4
+ username = ARGV.shift
5
+ File.open(File.join(dir, ".lock"), "w+") do |lock|
6
+ lock.flock(File::LOCK_EX)
7
+ begin
8
+ filename = File.join(dir, ".permissions")
9
+ permissions = File.read(filename).split("\n").map { |line| line.strip }.select { |line| line.split('=')[0] != username }
10
+ File.open(filename, "w") do |file|
11
+ permissions.each { |permission| file << permission << "\n" }
12
+ end
13
+ ensure
14
+ lock.flock(File::LOCK_UN)
15
+ end
16
+ end
File without changes
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ HUB=File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'hubbard'))
4
+
5
+ HUB_USERNAME=ENV['HUB_USERNAME']
6
+ fail "Please define HUB_USERNAME" unless HUB_USERNAME
7
+
8
+ # Ignore hostname passed in
9
+ ARGV.shift
10
+
11
+ $stderr.puts "#{HUB} #{HUB_USERNAME} #{ARGV.join(' ')}"
12
+ exec "#{HUB} #{HUB_USERNAME} #{ARGV.join(' ')}"
@@ -0,0 +1,183 @@
1
+ # Something in the Rakefile is generating
2
+ # a bunch of GIT_* environment variables,
3
+ # which mess everything up, so undo.
4
+ ENV.each do |key,value|
5
+ ENV[key] = nil if key =~ /^GIT_/
6
+ end
7
+
8
+ HUB=File.expand_path(File.join(File.dirname(__FILE__), "..", "bin", "hubbard"))
9
+
10
+ require 'fileutils'
11
+
12
+ ENV['HUB_USER'] = HUB_USER = "hub"
13
+ ENV['HUB_HOST'] = HUB_HOST = "example.com"
14
+ ENV['HUB_DATA'] = HUB_DATA = File.expand_path(File.join(File.dirname(__FILE__), '..', "data"))
15
+ ENV['GIT_SSH'] = File.expand_path(File.join(File.dirname(__FILE__), "gitssh"))
16
+
17
+ def hub(username, command, input=nil)
18
+ if input
19
+ result = `echo #{input} | #{HUB} #{username} #{command}`
20
+ else
21
+ result = `#{HUB} #{username} #{command}`
22
+ end
23
+ if $?.exitstatus != 0
24
+ raise "Command failed: hub #{username} #{command}\n#{result}"
25
+ end
26
+ result
27
+ end
28
+
29
+ def git(username, command)
30
+ ENV['HUB_USERNAME'] = username
31
+ result = `git #{command}`
32
+ if $?.exitstatus != 0
33
+ raise "Command failed: git #{command}:\n#{result}"
34
+ end
35
+ result
36
+ end
37
+
38
+ describe "Hubble" do
39
+ before(:each) do
40
+ FileUtils.rm_rf HUB_DATA
41
+ FileUtils.rm_rf "tmp"
42
+ end
43
+
44
+ it "should create project" do
45
+ hub("kipper", "create-project foo")
46
+ projects = hub("kipper", "list-projects").split("\n")
47
+ projects.should == ["foo"]
48
+ end
49
+
50
+ it "should not allow multiple projects with same name" do
51
+ hub("kipper", "create-project foo")
52
+ lambda { hub("kipper", "create-project foo") }.should raise_error
53
+ end
54
+
55
+ it "should delete project" do
56
+ hub("kipper", "create-project foo")
57
+ hub("kipper", "delete-project foo")
58
+
59
+ projects = hub("kipper", "list-projects").split("\n")
60
+ projects.should == []
61
+ end
62
+
63
+ it "should default to public project" do
64
+ hub("kipper", "create-project foo")
65
+
66
+ # Other users can see...
67
+ projects = hub("tiger", "list-projects").split("\n")
68
+ projects.should == ["foo"]
69
+
70
+ # But not delete
71
+ lambda { hub("tiger", "delete-project foo") }.should raise_error
72
+ end
73
+
74
+ it "should support private project" do
75
+ hub("kipper", "create-project foo --private")
76
+
77
+ # Other users can't see
78
+ projects = hub("tiger", "list-projects").split("\n")
79
+ projects.should == []
80
+ end
81
+
82
+ it "should create repositories" do
83
+ hub("kipper", "create-project foo")
84
+ hub("kipper", "create-repository foo bar")
85
+
86
+ repositories = hub("kipper", "list-repositories foo").split("\n")
87
+ repositories.length.should == 1
88
+ name,url = repositories[0].split
89
+ name.should == "bar"
90
+ url.should == "#{ENV['USER']}@#{HUB_HOST}:foo/bar.git"
91
+ end
92
+
93
+ def with_test_project
94
+ Dir.mkdir('tmp')
95
+ Dir.chdir('tmp') do
96
+ File.open("README", "w") { |f| f << "Hello, world\n" }
97
+ fail unless system "git init"
98
+ fail unless system "git add README"
99
+ fail unless system "git commit -m 'initial commit'"
100
+ yield
101
+ end
102
+ end
103
+
104
+ it "should allow git push" do
105
+ hub("kipper", "create-project foo")
106
+ hub("kipper", "create-repository foo bar")
107
+
108
+ with_test_project do
109
+ git("kipper", "push #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master")
110
+ end
111
+ end
112
+
113
+ it "should allow git push with write permissions" do
114
+ hub("kipper", "create-project foo")
115
+ hub("kipper", "add-permission foo tiger write")
116
+ hub("kipper", "create-repository foo bar")
117
+
118
+ with_test_project do
119
+ git("tiger", "push #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master")
120
+ end
121
+ end
122
+
123
+ it "should not allow git push with read permissions" do
124
+ hub("kipper", "create-project foo")
125
+ hub("kipper", "add-permission foo tiger read")
126
+ hub("kipper", "create-repository foo bar")
127
+
128
+ with_test_project do
129
+ lambda { git("tiger", "push #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master") }.should raise_error
130
+ end
131
+ end
132
+
133
+ it "should allow git pull" do
134
+ hub("kipper", "create-project foo")
135
+ hub("kipper", "create-repository foo bar")
136
+
137
+ with_test_project do
138
+ git("kipper", "push #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master")
139
+ git("kipper", "pull #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master")
140
+ end
141
+ end
142
+
143
+ it "should not allow git pull with no permissions" do
144
+ hub("kipper", "create-project foo --private")
145
+ hub("kipper", "create-repository foo bar")
146
+
147
+ with_test_project do
148
+ git("kipper", "push #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master")
149
+ lambda { git("tiger", "pull #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master") }.should raise_error
150
+ end
151
+ end
152
+
153
+ it "should allow git pull with read permissions" do
154
+ hub("kipper", "create-project foo")
155
+ hub("kipper", "create-repository foo bar")
156
+
157
+ with_test_project do
158
+ git("kipper", "push #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master")
159
+ git("tiger", "pull #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master")
160
+ end
161
+ end
162
+
163
+ it "should remove permission" do
164
+ hub("kipper", "create-project foo")
165
+ hub("kipper", "create-repository foo bar")
166
+ hub("kipper", "add-permission foo tiger read")
167
+ hub("kipper", "remove-permission foo tiger")
168
+
169
+ with_test_project do
170
+ lambda { git("tiger", "push #{ENV['USER']}@#{HUB_HOST}:foo/bar.git master") }.should raise_error
171
+ end
172
+ end
173
+
174
+ it "should add ssh key" do
175
+ hub("kipper", "add-key laptop", "ssh-rsa yabbadabba fdsa")
176
+ end
177
+
178
+ it "should allow admin to run-as another user" do
179
+ hub("admin", "run-as kipper create-project foo")
180
+ projects = hub("kipper", "list-projects").split("\n")
181
+ projects.should == ["foo"]
182
+ end
183
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'hubbard'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hubbard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Matthew Foemmel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-18 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ description: Hubbard is a command line tool for managing git repositories.
26
+ email: git@foemmel.com
27
+ executables:
28
+ - hubbard
29
+ - hubbard~
30
+ - hubbard
31
+ extensions: []
32
+
33
+ extra_rdoc_files:
34
+ - LICENSE
35
+ - README.md
36
+ files:
37
+ - .document
38
+ - .gitignore
39
+ - LICENSE
40
+ - README.md
41
+ - Rakefile
42
+ - VERSION
43
+ - bin/hubbard
44
+ - commands/add-key.rb
45
+ - commands/add-permission.rb
46
+ - commands/create-project.rb
47
+ - commands/create-repository.rb
48
+ - commands/delete-project.rb
49
+ - commands/git-receive-pack.rb
50
+ - commands/git-upload-pack.rb
51
+ - commands/help.rb
52
+ - commands/list-keys.rb
53
+ - commands/list-permissions.rb
54
+ - commands/list-projects.rb
55
+ - commands/list-repositories.rb
56
+ - commands/remove-key.rb
57
+ - commands/remove-permission.rb
58
+ - lib/hubbard.rb
59
+ - spec/gitssh
60
+ - spec/hubbard_spec.rb
61
+ - spec/spec.opts
62
+ - spec/spec_helper.rb
63
+ has_rdoc: true
64
+ homepage: http://github.com/mfoemmel/hubbard
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --charset=UTF-8
70
+ require_paths:
71
+ - ""
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: "0"
83
+ version:
84
+ requirements: []
85
+
86
+ rubyforge_project:
87
+ rubygems_version: 1.3.5
88
+ signing_key:
89
+ specification_version: 3
90
+ summary: Hubbard is a command line tool for managing git repositories.
91
+ test_files:
92
+ - spec/hubbard_spec.rb
93
+ - spec/spec_helper.rb