ginst 0.0.0 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/CHANGELOG +3 -0
  2. data/Manifest +35 -0
  3. data/README.txt +1 -0
  4. data/Rakefile +17 -54
  5. data/bin/ginst +12 -0
  6. data/bin/ginst_master +11 -0
  7. data/ginst.gemspec +47 -0
  8. data/lib/app/authorization.rb +44 -0
  9. data/lib/app/views/assets/jquery.corners.js +7 -0
  10. data/lib/app/views/assets/jquery.js +19 -0
  11. data/lib/app/views/branch_index.haml +4 -0
  12. data/lib/app/views/clean_layout.haml +17 -0
  13. data/lib/app/views/commit.haml +8 -0
  14. data/lib/app/views/commits.haml +8 -0
  15. data/lib/app/views/full_commit.haml +38 -0
  16. data/lib/app/views/index.haml +1 -0
  17. data/lib/app/views/layout.haml +32 -0
  18. data/lib/app/views/not_found.haml +3 -0
  19. data/lib/app/views/project.haml +1 -0
  20. data/lib/app/views/project_index.haml +11 -0
  21. data/lib/app/views/style.sass +107 -0
  22. data/lib/app/webserver.rb +113 -0
  23. data/lib/ginst/command_line.rb +45 -0
  24. data/lib/ginst/ginst.rb +73 -0
  25. data/lib/ginst/master_command_line.rb +40 -0
  26. data/lib/ginst/plugin.rb +16 -0
  27. data/lib/ginst/project.rb +40 -0
  28. data/lib/ginst/server.rb +51 -0
  29. data/lib/ginst/templates/rgithook.rb +6 -0
  30. data/lib/ginst.rb +12 -0
  31. data/test/app/test_webserver.rb +21 -0
  32. data/test/test_ginst.rb +84 -0
  33. data/test/test_helper.rb +36 -0
  34. data/test/test_project.rb +8 -0
  35. data/test/test_server.rb +75 -0
  36. data.tar.gz.sig +4 -0
  37. metadata +131 -35
  38. metadata.gz.sig +0 -0
  39. data/.document +0 -5
  40. data/.gitignore +0 -21
  41. data/LICENSE +0 -20
  42. data/README.rdoc +0 -18
  43. data/features/ginst.feature +0 -9
  44. data/features/step_definitions/ginst_steps.rb +0 -0
  45. data/features/support/env.rb +0 -4
  46. data/spec/ginst_spec.rb +0 -7
  47. data/spec/spec.opts +0 -1
  48. data/spec/spec_helper.rb +0 -9
@@ -0,0 +1,107 @@
1
+ body
2
+ margin: 0
3
+ padding: 0
4
+ #container
5
+ width: 80%
6
+ margin: 0 auto
7
+ #header
8
+ padding: 0 10px
9
+ margin-bottom: 48px
10
+ #logo
11
+ width: 150px
12
+ text-align: center
13
+ margin-bottom: 20px
14
+ h1
15
+ width: 150px
16
+ text-align: center
17
+ margin: 0
18
+ padding: 10px 0px 0px 0px
19
+ font-family: Futura, Verdana, Arial, Helvetica, sans-serif
20
+ font-size: 32px
21
+ h2
22
+ width: 150px
23
+ text-align: center
24
+ font-family: Arial, Helvetica, sans-serif
25
+ font-size: 12px
26
+ padding-bottom: 0px
27
+ margin-top: 0px
28
+ #left
29
+ float: left
30
+ width: 12em
31
+ padding: 0
32
+ overflow: hidden
33
+ ul
34
+ margin: 0px 0px 24px 5px
35
+ padding: 0px
36
+ list-style-type: none
37
+ font-family: Verdana, Geneva, sans-serif
38
+ text-transform: uppercase
39
+ line-height: 24px
40
+ a
41
+ text-decoration: none
42
+ color: black
43
+ ul
44
+ margin: 0px 0px 24px 25px
45
+ padding: 0px
46
+ list-style-type: none
47
+ font-family: Verdana, Geneva, sans-serif
48
+ text-transform: lowercase
49
+ line-height: 18px
50
+ #mainContent
51
+ margin: 0 20px 0 13em
52
+ .separator
53
+ margin: 0px 0px 6px 0px
54
+ padding: 3px
55
+ line-height: 24px
56
+ list-style-type: none
57
+ font-family: Arial, Helvetica, sans-serif
58
+ list-style-type: none
59
+ border-bottom: 1px dotted #666
60
+ padding-bottom: 0px
61
+ margin-bottom: 12px
62
+ font-weight: bolder
63
+ .success
64
+ background: #7CC979
65
+ .notest
66
+ background: #CCCC66
67
+ .fail
68
+ background: #BF6A6A
69
+ .commit
70
+ margin: 0px 0px 6px 0px
71
+ padding: 3px
72
+ line-height: 24px
73
+ list-style-type: none
74
+ height: 48px
75
+ font-family: Arial, Helvetica, sans-serif
76
+ overflow: hidden
77
+ img
78
+ float: left
79
+ width: 42px
80
+ margin: 3px
81
+ .sha1, .message, .author, .time
82
+ margin: 0px 3px 0px 3px
83
+ .sha1
84
+ float: left
85
+ .message
86
+ margin-left: 120px
87
+ font-weight: bolder
88
+ .author a
89
+ text-decoration: none
90
+ color: #333
91
+ .author
92
+ float: left
93
+ .time
94
+ text-align: right
95
+ color: #333
96
+ #footer
97
+ font-family: Arial, Helvetica, sans-serif
98
+ clear: both
99
+ padding: 0 10px
100
+ background: #DDDDDD
101
+ p
102
+ margin: 0 auto
103
+ text-align: center
104
+ padding: 10px 0
105
+ .version
106
+ font-size: smaller
107
+
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'sinatra'
4
+ require 'optparse'
5
+ require 'ruby-debug'
6
+ require 'digest/md5'
7
+ require File.join(File.dirname(__FILE__),'authorization')
8
+
9
+ begin
10
+ require 'ginst'
11
+ require 'ginst/plugin'
12
+ rescue LoadError
13
+ require File.join(File.dirname(__FILE__),'..','ginst')
14
+ end
15
+
16
+
17
+ set :views, File.dirname(__FILE__) + '/views'
18
+ set :public, File.dirname(__FILE__) + '/views/assets'
19
+
20
+ helpers do
21
+ def format_message(text)
22
+ text.gsub!("\t",'  ')
23
+ text.gsub!("\n",'<br/>')
24
+ text
25
+ end
26
+
27
+ def redirect_back
28
+ redirect request.referer
29
+ end
30
+ end
31
+
32
+ def load_ginst
33
+ @ginst = Ginst::Ginst.new(ENV["REPO_PATH"] || Dir.pwd)
34
+ #@ginst.add_project '~/Documents/ginst'
35
+ end
36
+
37
+ def load_project
38
+ load_ginst
39
+ @project = @ginst[params[:project] || @ginst.projects.first ] or raise Sinatra::NotFound
40
+ end
41
+
42
+ def load_branch
43
+ load_project
44
+ @branch = @project.branch(params[:branch] || 'master')
45
+ @per_page = (params[:per_page] && params[:per_page].to_i) || 30
46
+ @page = (params[:page] && params[:page].to_i) || 0
47
+ @commits = @project.repo.commits(@branch.commit.id,@per_page, @page*@per_page)
48
+ end
49
+
50
+ def load_commit
51
+ load_project
52
+ @commit = @project.repo.commit(params[:commit])
53
+ end
54
+
55
+
56
+ get '/' do
57
+ load_ginst
58
+ redirect @ginst.projects.first.name
59
+ load_ginst
60
+ haml :index
61
+ end
62
+
63
+ get '/:project' do
64
+ load_project
65
+ haml :project_index
66
+ end
67
+
68
+ post '/:project' do
69
+ load_project
70
+ @project.install_ginst
71
+ redirect_back
72
+ end
73
+
74
+ get '/:project/branch/:branch' do
75
+ load_branch
76
+ haml :branch_index
77
+ end
78
+
79
+ get '/:project/commit/:commit' do
80
+ load_commit
81
+ haml :full_commit
82
+ end
83
+
84
+ post '/:project/commit/:commit' do
85
+ load_commit
86
+ @project.run_hooks_for_commit(@commit)
87
+ redirect_back
88
+ end
89
+
90
+ not_found do
91
+ haml :not_found, :layout => :clean_layout
92
+ end
93
+
94
+ error do
95
+ 'Sorry there was a nasty error - ' + env['sinatra.error'].name
96
+ end
97
+
98
+ error Exception do
99
+ 'Sorry there was a nasty error - ' + env['sinatra.error'].name
100
+ end
101
+
102
+ get '/about' do
103
+ "I'm running on Version " + Sinatra::VERSION
104
+ end
105
+
106
+ get '/style.css' do
107
+ content_type 'text/css', :charset => 'utf-8'
108
+ # css_files = Dir.glob(File.join(File.dirname(File.expand_path(__FILE__)),'views','assets','*.css'))
109
+ # css_files.map{|f| File.read f}.join("\n")
110
+ sass :style
111
+ end
112
+
113
+
@@ -0,0 +1,45 @@
1
+ require 'optparse'
2
+ require 'grit'
3
+ module Ginst
4
+ class CommandLine
5
+ def self.run
6
+ options = parse()
7
+ puts %x(#{$0} --help) unless options[:command]
8
+ exit(0) if options[:command] == :exit || !options[:command]
9
+
10
+ if [:start,:stop,:restart,:status].include? options[:command]
11
+ Server.send(options[:command],options)
12
+ Process.waitall
13
+ Server.status(options) unless options[:command] == :status
14
+ end
15
+
16
+ Ginst.install(options) if options[:command] == :install
17
+
18
+ rescue OptionParser::InvalidOption, OptionParser::AmbiguousOption
19
+ puts "Invalid options #{ARGV}"
20
+ puts %x(#{$0} --help)
21
+ rescue Grit::InvalidGitRepositoryError
22
+ puts "Invalid path #{options[:path]}. Not found a git repo"
23
+ end
24
+
25
+ def self.parse
26
+ options = {}
27
+ OptionParser.new do |opts|
28
+ opts.banner = "Usage: #{File.basename($0)} [options]"
29
+
30
+ opts.on_head('-s','--start','Start the web server') { options[:command] = :start }
31
+ opts.on_head('-t','--stop', 'Stop the web server') { options[:command] = :stop }
32
+ opts.on_head('-r','--restart','Restart the web server') { options[:command] = :restart }
33
+ opts.on_head('-a','--status', 'Show status of the web server') { options[:command] = :status }
34
+ opts.on_head('-p','--port PORT','Webserver port') {|p| options[:port] = p.to_i }
35
+ opts.on_head('-i','--install [PATH]', 'Install ginst') {|p| options[:command] = :install ; p and options[:path] = p }
36
+ opts.on('-p', '--path GIT_WORK_DIR', 'Specify other path (default current dir)'){|p| options[:path] = p}
37
+ opts.on_tail("-h", "--help", 'Show help') { options[:command]=:exit; puts opts }
38
+ opts.on_tail("-v", "--version", 'Show version') { options[:command]=:exit; puts "Ginst version #{Ginst::Version}" }
39
+ end.parse!
40
+
41
+ options[:path] ||= Dir.pwd
42
+ options
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,73 @@
1
+ require 'fileutils'
2
+ require 'rgithook'
3
+ require 'ginst/project'
4
+
5
+ module Ginst
6
+ class Ginst
7
+
8
+ attr_reader :projects
9
+ def initialize(path = nil)
10
+ @projects = [Project.new(path || Dir.pwd)]
11
+ end
12
+
13
+ def [](project_name)
14
+ @projects.select{|p| p.name == project_name}.first
15
+ end
16
+
17
+ def add_project(path_or_project)
18
+ case path_or_project
19
+ when String
20
+ @projects << Project.new(path_or_project)
21
+ when Project
22
+ @projects << path_or_project
23
+ end
24
+ end
25
+ # Install Ginst in the given path
26
+ # Options can be:
27
+ # * :verbose: true/false
28
+ def self.install(path,options={})
29
+ self.new(path).install(options)
30
+ end
31
+
32
+ def install(options = {})
33
+ opts={:verbose => true, :interactive => true}
34
+ opts.merge(options)
35
+
36
+ puts 'Installing rgithook' if opts[:verbose]
37
+ @rgithook = RGitHook::RGitHook.new(@path)
38
+ @rgithook.install(opts[:interactive])
39
+
40
+ #check for a valid repo
41
+ @path = Grit::Repo.new(@path).path
42
+ @ginst_dir = File.join(@path,'ginst')
43
+
44
+ puts 'Installing ginst' if opts[:verbose]
45
+ install_ginst(opts)
46
+
47
+ puts 'Installing hooks' if opts[:verbose]
48
+ install_hook(opts)
49
+ end
50
+
51
+ def self.install_master(path,options)
52
+ self.new(path).install_mater(options)
53
+ end
54
+
55
+ private
56
+
57
+ def install_ginst(opts)
58
+ puts "Installing Ginst in #{@path}" if opts[:verbose]
59
+ FileUtils.mkdir(@ginst_dir) unless File.directory? @ginst_dir
60
+ FileUtils.cp_r(template_path,@ginst_dir, {:preserve => true, :verbose => opts[:verbose]})
61
+ end
62
+
63
+ def install_hook(opts)
64
+ puts
65
+ @rgithook.conf_file
66
+ end
67
+
68
+
69
+ def template_path
70
+ File.join(File.dirname(__FILE__),'..','app','.')
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,40 @@
1
+ require 'optparse'
2
+ require 'ruby-debug'
3
+ module Ginst
4
+ class CommandLine
5
+ def self.run
6
+ options = parse()
7
+ puts %x(#{$0} --help) unless options[:command]
8
+ exit(0) if options[:command] == :exit || !options[:command]
9
+
10
+ if [:start,:stop,:restart,:status].include? options[:command]
11
+ Server.send(options[:command])
12
+ Process.waitall
13
+ Server.status unless options[:command] == :status
14
+ end
15
+
16
+ rescue OptionParser::InvalidOption, OptionParser::AmbiguousOption
17
+ puts "Invalid options #{ARGV}"
18
+ puts %x(#{$0} --help)
19
+ end
20
+
21
+ def self.parse
22
+ options = {}
23
+ OptionParser.new do |opts|
24
+ opts.banner = "Usage: #{File.basename($0)} [options]"
25
+
26
+ opts.on_head('-s','--start','Start the web server') { options[:command] = :start }
27
+ opts.on_head('-t','--stop', 'Stop the web server') { options[:command] = :stop }
28
+ opts.on_head('-r','--restart','Restart the web server') { options[:command] = :restart }
29
+ opts.on_head('-a','--status', 'Show status of the web server') { options[:command] = :status }
30
+ opts.on_head('-i','--install [PATH]', 'Install ginst') {|p| options[:command] = :install ; p and options[:path] = p }
31
+ opts.on('-p', '--path GIT_WORK_DIR', 'Specify other path (default current dir)'){|p| options[:path] = p}
32
+ opts.on_tail("-h", "--help", 'Show help') { options[:command]=:exit; puts opts }
33
+ opts.on_tail("-v", "--version", 'Show version') { options[:command]=:exit; puts "Ginst version #{Ginst::Version}" }
34
+ end.parse!
35
+
36
+ options[:path] ||= Dir.pwd
37
+ options
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Ginst
3
+ class GinstPlugin < ::RGitHook::Plugin
4
+
5
+ module RunnerMethods
6
+ def ginst_post_receive(old_commit,new_commit,ref)
7
+ repo.commits_between(old_commit,new_commit).each do |commit|
8
+ test(commit)
9
+ end
10
+
11
+ %x(say 'hello')
12
+ repo.commit(old_commit).properties[:gritted] = "YES"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+
2
+ module Ginst
3
+ class Project
4
+ include RGitHook
5
+ attr_reader :path, :rgithook, :repo, :name
6
+ def initialize(path)
7
+ @path = path
8
+ @rgithook = RGitHook.new(path)
9
+ @repo = @rgithook.repo
10
+ @name = @rgithook.project_name
11
+ end
12
+
13
+ def branches
14
+ @repo.branches
15
+ end
16
+
17
+ def branch(branch_name)
18
+ @repo.branches.select{|b| b.name == branch_name}.first
19
+ end
20
+
21
+ def commits(branch_name)
22
+ @repo.commits(branch(branch_name).sha)
23
+ end
24
+
25
+ def install_ginst
26
+ @rgithook.install(false)
27
+ File.open(@rgithook.hooks_file,'w') do |f|
28
+ f.write File.read(File.join(File.dirname(__FILE__),'templates','rgithook.rb'))
29
+ f.close
30
+ end
31
+ @rgithook = RGitHook.new(@path)
32
+ end
33
+
34
+ def run_hooks_for_commit(commit)
35
+ @rgithook.post_receive(commit.parents.first.sha,commit.sha,'any')
36
+ end
37
+ end
38
+ end
39
+
40
+
@@ -0,0 +1,51 @@
1
+ require 'rubygems'
2
+ require 'daemons'
3
+
4
+ module Ginst
5
+ class Server
6
+ DEFAULT_PORT = 7654
7
+
8
+ # Options are:
9
+ # :port => Web server port
10
+ # :path => Repo path
11
+ def self.start(options = {})
12
+ puts "Starting ginst for #{options[:path]} at http://localhost:#{options[:port] || DEFAULT_PORT}"
13
+ fork{run_daemon_with_env(options[:path],['start','--',sinatra_options(options)])}
14
+ end
15
+
16
+ def self.stop(options = {})
17
+ fork{run_daemon_with_env(options[:path],['stop'])}
18
+ end
19
+
20
+ def self.restart(options = {})
21
+ puts "Restarting ginst for #{options[:path]} at http://localhost:#{options[:port] || DEFAULT_PORT}"
22
+ run_daemon_with_env(options[:path],['restart','--',sinatra_options(options)])
23
+ end
24
+
25
+ def self.status(options = {})
26
+ run_daemon_with_env(options[:path],['status']).show_status.to_s
27
+ end
28
+
29
+ private
30
+
31
+ def self.run_daemon_with_env(path,env)
32
+ rgithook = ::RGitHook::RGitHook.new(path)
33
+ daemons_options = {:app_name => "ginst-#{rgithook.project_name}", :dir_mode=>:normal, :dir=>rgithook.path, :ARGV => env, :multiple => false, :log_output => true, :monitor => true, :mode => :exec}
34
+ ENV['REPO_PATH'] = path
35
+ Daemons.run(server_path(path),daemons_options)
36
+ end
37
+
38
+ def self.sinatra_options(options)
39
+ sinatra_options = "-p #{options.delete(:port) || DEFAULT_PORT} -e production"
40
+ end
41
+
42
+ # Try to load de server of the local repo .git/ginst/webserver
43
+ # but if it does not exists, load the default
44
+ def self.server_path(path)
45
+ local_file = File.join(Grit::Repo.new(path).path,'ginst','webserver.rb')
46
+ system_file= File.expand_path(File.join(File.dirname(__FILE__),'..','app','webserver.rb'))
47
+ File.file?(local_file) ? local_file : system_file
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,6 @@
1
+ require 'ginst'
2
+ require 'ginst/plugin'
3
+
4
+ on :post_receive, :background => true do |old_commit,new_commit,ref|
5
+ ginst_post_receive(old_commit,new_commit,ref)
6
+ end
data/lib/ginst.rb CHANGED
@@ -0,0 +1,12 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ module Ginst
4
+ VERSION = '0.1.1'
5
+ end
6
+
7
+ require 'rubygems'
8
+ require 'rgithook'
9
+
10
+ Ginst.autoload(:Ginst, 'ginst/ginst')
11
+ Ginst.autoload(:Server, 'ginst/server')
12
+ Ginst.autoload(:CommandLine, 'ginst/command_line')
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'sinatra/test/unit'
4
+ require File.dirname(__FILE__) + '/../../lib/app/webserver'
5
+
6
+ class SinatraServer < Test::Unit::TestCase
7
+ def test_index
8
+ mock_ginst
9
+ get '/'
10
+ end
11
+ def test_project_index
12
+ mock_ginst
13
+ get '/test_project'
14
+ end
15
+
16
+ def test_branch_index
17
+ mock_ginst
18
+ get '/test_project/master'
19
+ end
20
+
21
+ end
@@ -0,0 +1,84 @@
1
+ require 'test_helper'
2
+
3
+ class TestGinst < Test::Unit::TestCase
4
+ include Ginst
5
+
6
+ def test_new
7
+ assert_instance_of Ginst, Ginst.new
8
+
9
+ end
10
+
11
+ def test_find_project
12
+ mock_repo
13
+ Ginst.new
14
+ end
15
+ end
16
+
17
+ # attr_reader :projects
18
+ # def initialize(path = nil)
19
+ # @projects = [Project.new(path || Dir.pwd)]
20
+ # end
21
+ #
22
+ # def [](project_name)
23
+ # @projects.select{|p| p.name == project_name}.first
24
+ # end
25
+ #
26
+ # def add_project(path_or_project)
27
+ # case path_or_project
28
+ # when String
29
+ # @projects << Project.new(path_or_project)
30
+ # when Project
31
+ # @projects << path_or_project
32
+ # end
33
+ # end
34
+ # # Install Ginst in the given path
35
+ # # Options can be:
36
+ # # * :verbose: true/false
37
+ # def self.install(path,options={})
38
+ # self.new(path).install(options)
39
+ # end
40
+ #
41
+ # def install(options = {})
42
+ # opts={:verbose => true, :interactive => true}
43
+ # opts.merge(options)
44
+ #
45
+ # puts 'Installing rgithook' if opts[:verbose]
46
+ # @rgithook = RGitHook::RGitHook.new(@path)
47
+ # @rgithook.install(opts[:interactive])
48
+ #
49
+ # #check for a valid repo
50
+ # @path = Grit::Repo.new(@path).path
51
+ # @ginst_dir = File.join(@path,'ginst')
52
+ #
53
+ # puts 'Installing ginst' if opts[:verbose]
54
+ # install_ginst(opts)
55
+ #
56
+ # puts 'Installing hooks' if opts[:verbose]
57
+ # install_hook(opts)
58
+ # end
59
+ #
60
+ # def self.install_master(path,options)
61
+ # self.new(path).install_mater(options)
62
+ # end
63
+ #
64
+ # private
65
+ #
66
+ # def install_ginst(opts)
67
+ # puts "Installing Ginst in #{@path}" if opts[:verbose]
68
+ # FileUtils.mkdir(@ginst_dir) unless File.directory? @ginst_dir
69
+ # FileUtils.cp_r(template_path,@ginst_dir, {:preserve => true, :verbose => opts[:verbose]})
70
+ # end
71
+ #
72
+ # def install_hook(opts)
73
+ # puts
74
+ # @rgithook.conf_file
75
+ # end
76
+ #
77
+ #
78
+ # def template_path
79
+ # File.join(File.dirname(__FILE__),'..','app','.')
80
+ # end
81
+ # end
82
+ # end
83
+ #
84
+ #
@@ -0,0 +1,36 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/ginst'
3
+ require 'rubygems'
4
+ require 'grit'
5
+ require 'mocha'
6
+
7
+ def mock_repo
8
+ repo = mock('Grit::Repo')
9
+ repo.stubs(:'is_a?').with(::Grit::Repo).returns(true)
10
+ repo.stubs(:path).with().returns('path')
11
+ repo.stubs(:bare).with().returns('false')
12
+
13
+ branch = mock('Grit::Branch')
14
+ branch.stubs(:name).with().returns('master')
15
+
16
+ repo.stubs(:branches).with().returns([branch])
17
+ Grit::Repo.stubs(:new).returns(repo)
18
+
19
+ repo
20
+ end
21
+
22
+ def mock_ginst
23
+ repo = mock_repo
24
+
25
+ ginst = mock('Ginst::Ginst')
26
+ ginst.stubs(:add_project)
27
+
28
+ project = mock('Ginst::Project')
29
+ project.stubs(:name).returns('test_project')
30
+ project.stubs(:branches).returns(repo.branches)
31
+ project.stubs(:branch).with('master').returns([])
32
+
33
+ ginst.stubs(:[]).returns(project)
34
+ ginst.stubs(:projects).returns([project])
35
+ Ginst::Ginst.stubs(:new).returns(ginst)
36
+ end
@@ -0,0 +1,8 @@
1
+
2
+
3
+ class TestProject < Test::Unit::TestCase
4
+
5
+
6
+ def test_something
7
+ end
8
+ end