ginst 0.1.3 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data.tar.gz.sig +0 -0
  2. data/Manifest +34 -8
  3. data/Rakefile +6 -3
  4. data/bin/ginst +11 -4
  5. data/features/browsing_project.feature +20 -0
  6. data/features/manage_projects.feature +36 -0
  7. data/features/manage_server.feature +17 -0
  8. data/features/step_definitions/manage_projects_steps.rb +35 -0
  9. data/features/step_definitions/manage_server_steps.rb +33 -0
  10. data/features/step_definitions/webrat_steps.rb +99 -0
  11. data/features/support/env.rb +29 -0
  12. data/features/support/git.rb +22 -0
  13. data/features/support/paths.rb +16 -0
  14. data/ginst.gemspec +8 -11
  15. data/lib/app/authorization.rb +1 -1
  16. data/lib/app/views/_commit.haml +8 -0
  17. data/lib/app/views/commit.haml +38 -8
  18. data/lib/app/views/commits.haml +1 -1
  19. data/lib/app/views/edit.haml +11 -0
  20. data/lib/app/views/layout.haml +10 -7
  21. data/lib/app/views/new.haml +19 -0
  22. data/lib/app/views/project.haml +4 -1
  23. data/lib/app/views/ref.haml +3 -0
  24. data/lib/app/views/style.sass +0 -1
  25. data/lib/app/webserver.rb +66 -76
  26. data/lib/ginst.rb +7 -5
  27. data/lib/ginst/core_extensions.rb +32 -0
  28. data/lib/ginst/ginst.rb +5 -72
  29. data/lib/ginst/project.rb +13 -38
  30. data/lib/ginst/project/base.rb +59 -0
  31. data/lib/ginst/project/build.rb +117 -0
  32. data/lib/ginst/project/commit_db.rb +29 -0
  33. data/lib/ginst/project/finders.rb +17 -0
  34. data/lib/ginst/project/grit.rb +53 -0
  35. data/lib/ginst/project/validations.rb +60 -0
  36. data/lib/ginst/test.rb +8 -0
  37. data/lib/ginst/test/base.rb +17 -0
  38. data/lib/ginst/test/dir.rb +39 -0
  39. data/lib/ginst/test/repo.rb +13 -0
  40. data/lib/ginst/test/run.rb +11 -0
  41. data/lib/ginst/web_server.rb +43 -0
  42. data/spec/fixtures/sample_repo.zip +0 -0
  43. data/spec/models/project_base_spec.rb +56 -0
  44. data/spec/models/project_build_spec.rb +18 -0
  45. data/spec/models/project_commit_db_spec.rb +19 -0
  46. data/spec/models/project_finders_spec.rb +34 -0
  47. data/spec/models/project_grit_spec.rb +19 -0
  48. data/spec/models/project_validations_spec.rb +72 -0
  49. data/spec/spec_helper.rb +8 -0
  50. metadata +56 -29
  51. metadata.gz.sig +0 -0
  52. data/lib/app/views/full_commit.haml +0 -38
  53. data/lib/app/views/project_index.haml +0 -11
  54. data/lib/ginst/command_line.rb +0 -47
  55. data/lib/ginst/master_command_line.rb +0 -40
  56. data/lib/ginst/plugin.rb +0 -16
  57. data/lib/ginst/server.rb +0 -51
  58. data/lib/ginst/templates/rgithook.rb +0 -6
data/lib/ginst/project.rb CHANGED
@@ -1,40 +1,15 @@
1
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
2
+ # The load order is important so don't change !
3
+ require 'ginst/project/base'
4
+ require 'ginst/project/validations'
5
+ require 'ginst/project/finders'
6
+ require 'ginst/project/grit'
7
+ require 'ginst/project/commit_db'
8
+ require 'ginst/project/build'
9
+
10
+ class Ginst::Project
11
+ load_projects
38
12
  end
39
-
40
-
13
+
14
+
15
+
@@ -0,0 +1,59 @@
1
+ class Ginst::Project
2
+
3
+ class << self
4
+
5
+ def projects
6
+ @@projects
7
+ end
8
+
9
+ def save_all
10
+ f = File.open(File.join(Ginst::Ginst.working_dir,'projects.yml'),'w')
11
+ p = @@projects.dup.map do |p|
12
+ p.send(:instance_variables).each do |var|
13
+ p.send(:remove_instance_variable,var) unless %w(@name @dir @build_process).include? var
14
+ end
15
+ p
16
+ end
17
+ f.write(p.to_yaml)
18
+ f.close
19
+ true
20
+ end
21
+
22
+ def load_projects
23
+ projects = YAML::load(File.read(File.join(Ginst::Ginst.working_dir,'projects.yml')))
24
+ projects = [] unless projects.kind_of? Array
25
+ if projects.empty? || projects.map{|c| c.class}.uniq == [Ginst::Project]
26
+ @@projects = projects
27
+ else
28
+ STDERR.write "There is an error in projects.yml"
29
+ @@projects = []
30
+ end
31
+ rescue Errno::ENOENT
32
+ @@projects = []
33
+ end
34
+ end
35
+
36
+ attr_accessor :name, :dir
37
+
38
+ # Generate a new project
39
+ # Ginst::Project.new("name" => "My Super Project", "dir" => "asdf")
40
+ def initialize(params = {})
41
+ params.each{|k,v| send("#{k}=".to_sym, v) }
42
+ @errors = {}
43
+ end
44
+
45
+ def save
46
+ if self.class.projects.select{|p| p == self}.empty?
47
+ self.class.projects << self
48
+ end
49
+ self.class.save_all
50
+ end
51
+
52
+ def destroy
53
+ unless self.class.projects.select{|p| p == self}.empty?
54
+ self.class.projects.delete(self)
55
+ self.class.save_all
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,117 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+ require 'open3'
4
+
5
+ class Ginst::Project
6
+
7
+ DEFAULT_BUILD_PROCESS =<<-EOBP
8
+ rake = `rake`
9
+ {:rake => rake, :status => $?.to_i }
10
+ EOBP
11
+
12
+ attr_accessor :build_process
13
+
14
+ def build_process
15
+ @build_process || DEFAULT_BUILD_PROCESS
16
+ end
17
+
18
+ class Commit
19
+
20
+ def status
21
+ if self.build[:status]
22
+ [true,'0',0].include?(self.build[:status]) ? 'success' : 'fail'
23
+ else
24
+ 'notest'
25
+ end
26
+ end
27
+
28
+ # Build the current commit
29
+ # Checkouts the project in a temp dir and tries to build it
30
+ # It store the build information in the ginst commits db
31
+ # The returning hash can contain the next keys
32
+ # * :status => 'success' | 'fail'
33
+ # * :git_clone => Output of the git clone command
34
+ # * :git_checkout => The output of the git co command
35
+ # * :git_clean => (opt) The output of the git clean command
36
+ # * :exception => (opt) The exception generated at eval code
37
+ # * :eval_finished => If the build_process makes a return
38
+ # * :eval => (opt) If the eval doesn't return a Hash, its return value will be store here and status will be fail
39
+ def build!
40
+ current_dir = Dir.pwd
41
+
42
+ build_data = {:build_process => @project.build_process}
43
+ dir = File.join('tmp',@project.name.slug,self.id[0..1],self.id[2..-1])
44
+ build_data[:dir] = dir
45
+
46
+ if File.exists? dir
47
+ FileUtils.rm_rf(dir)
48
+ end
49
+
50
+ FileUtils.mkdir_p(File.dirname(dir))
51
+
52
+ build_data[:git_clone] = run_with_capture("git clone -s #{@project.dir} #{dir}")
53
+ raise "Git clone error #{build_data[:git_clone]}" unless $? == 0
54
+
55
+ Dir.chdir dir
56
+ build_data[:git_checkout] = run_with_capture("git checkout #{self.id}")
57
+ raise "Git checkout error #{build_data.inspect}" unless $? == 0
58
+
59
+ build_data[:git_clean] = `rm -Rf .git`
60
+
61
+ # Do the build process
62
+ build_data.merge! build_eval
63
+
64
+ build_data[:status] = 'fail' unless build_data[:status]
65
+
66
+ puts build_data.inspect
67
+
68
+ build_data
69
+ ensure
70
+ Dir.chdir current_dir
71
+ self.build = build_data
72
+ end
73
+
74
+ private
75
+
76
+ def run_with_capture(cmd)
77
+ stdin,stdout,stderr = Open3.popen3(cmd)
78
+ [stdout.read, stderr.read]
79
+ end
80
+
81
+ def build_eval
82
+ build = {}
83
+ ret = real_eval
84
+ if Hash === ret
85
+ build = ret
86
+ else
87
+ build[:eval] = ret
88
+ end
89
+ build
90
+ rescue => e
91
+ build[:exception] = [e.inspect, e.backtrace]
92
+ build
93
+ end
94
+
95
+ def real_eval
96
+ eval(@project.build_process,Object.new.send(:binding),"#{@project.name.slug}-build",0)
97
+ end
98
+
99
+ def create_temp_dir
100
+ dir = random_dir_name while (File.exists?(dir = random_dir_name))
101
+ FileUtils.mkdir_p(dir)
102
+ dir
103
+ end
104
+
105
+ def random_dir_name
106
+ File.join(Dir.tmpdir,"ginst-#{Time.now.usec}#{rand(999999999)}")
107
+ end
108
+
109
+ private
110
+ def fixture_path(fixture_name)
111
+ file = File.join(::RGitHook::PATH,'..','spec','fixtures',fixture_name)
112
+ file if File.file?(file) or raise "Not found fixture in #{file}"
113
+ end
114
+
115
+ end
116
+
117
+ end
@@ -0,0 +1,29 @@
1
+ require 'dbm'
2
+ class Ginst::Project
3
+
4
+ class Commit
5
+
6
+ # get build information
7
+ def build
8
+ if self.db[self.id]
9
+ ret = Marshal.load(self.db[self.id])
10
+ Hash === ret ? ret : {}
11
+ else
12
+ {}
13
+ end
14
+ rescue
15
+ {}
16
+ end
17
+
18
+ def build=(args = {})
19
+ self.db[id] = Marshal.dump(args)
20
+ self.db.close
21
+ @db = nil
22
+ end
23
+
24
+ protected
25
+ def db
26
+ @db ||= DBM.new(File.join(Ginst::Ginst.working_dir,@project.name.slug))
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ class Ginst::Project
2
+
3
+ class << self
4
+ def find_by_name(name)
5
+ find_first_filter{|p| p.name == name }
6
+ end
7
+
8
+ def find_by_slug(slug)
9
+ find_first_filter{|p| p.name.slug == slug}
10
+ end
11
+
12
+ private
13
+ def find_first_filter
14
+ projects.select{|p| yield p}.first
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,53 @@
1
+ require 'grit'
2
+ require 'cgi'
3
+ class Ginst::Project
4
+ class Ref < Struct.new(:project, :repo,:name,:grit_commit)
5
+ def commits
6
+ repo.commits(grit_commit.id).map{|c| Commit.new(project, c) }
7
+ end
8
+
9
+ def commit
10
+ self.grit_commit.id
11
+ end
12
+ end
13
+
14
+ class Commit < Grit::Commit
15
+ def initialize(project,c)
16
+ @project = project
17
+ c.instance_variables.each do |var|
18
+ instance_variable_set(var, c.send(:instance_variable_get, var))
19
+ end
20
+ end
21
+ end
22
+
23
+ # Return an array of Refs
24
+ def refs
25
+ repo.refs.map{ |ref|
26
+ Ref.new(self,repo,ref.name,ref.commit)
27
+ }
28
+ end
29
+
30
+
31
+ def find_ref(name)
32
+ refs.select{|r| r.name == name}.first || nil
33
+ end
34
+
35
+ def find_commit(commit_id)
36
+ commit = repo.commit(commit_id) and Commit.new(self,commit)
37
+ rescue
38
+ nil
39
+ end
40
+
41
+ protected
42
+
43
+ def repo
44
+ @repo ||= Grit::Repo.new(@dir)
45
+ rescue
46
+ []
47
+ end
48
+
49
+ def commit(id)
50
+ repo.commit(id)
51
+ end
52
+
53
+ end
@@ -0,0 +1,60 @@
1
+ class Ginst::Project
2
+ attr_reader :errors
3
+
4
+ def errors
5
+ @errors ||= {}
6
+ end
7
+
8
+ def valid?
9
+ @errors = {}
10
+ validate_name
11
+ validate_dir
12
+ validate_uniqueness
13
+ @errors.empty?
14
+ end
15
+
16
+ def validate_dir
17
+ self.dir = File.expand_path(self.dir)
18
+ @repo = ::Grit::Repo.new(self.dir)
19
+ true
20
+ rescue ::Grit::NoSuchPathError
21
+ @errors['dir'] = "#{self.dir} not found in #{`hostname`}."
22
+ false
23
+ rescue ::Grit::InvalidGitRepositoryError
24
+ @errors['dir'] = "#{self.dir} is not a valid repo."
25
+ false
26
+ end
27
+
28
+ def validate_name
29
+ @errors["name"] = "Is needed" and return if self.name.nil?
30
+ @errors["name"] = "Can't be blank" if self.name.empty?
31
+ end
32
+
33
+ def validate_uniqueness
34
+ if self.name.kind_of? String
35
+ projects = self.class.projects.dup
36
+ projects.delete(self)
37
+ unless projects.select{|p| p.name.slug == self.name.slug }.first.nil?
38
+ @errors["name"] = "Name already taken"
39
+ end
40
+ end
41
+
42
+ if self.dir.kind_of? String
43
+ projects = self.class.projects.dup
44
+ projects.delete(self)
45
+ unless projects.select{|p| p.dir == self.dir }.first.nil?
46
+ @errors["dir"] = "There is already one project with the same directory"
47
+ end
48
+ end
49
+ end
50
+
51
+ alias :save_without_validations :save
52
+ def save
53
+ if valid?
54
+ save_without_validations
55
+ else
56
+ false
57
+ end
58
+ end
59
+
60
+ end
data/lib/ginst/test.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'tmpdir'
2
+ require 'fileutils'
3
+
4
+
5
+ require 'ginst/test/base'
6
+ require 'ginst/test/repo'
7
+ require 'ginst/test/dir'
8
+ require 'ginst/test/run'
@@ -0,0 +1,17 @@
1
+
2
+ module Ginst::Test
3
+ class << self
4
+ attr_accessor :ginst_gem_dir
5
+ end
6
+ @ginst_gem_dir = File.expand_path(File.join(File.dirname(__FILE__),['..','..','..']))
7
+
8
+
9
+
10
+ private
11
+
12
+ def fixture_path(fixture_name)
13
+ file = File.join(Ginst::Test.ginst_gem_dir,%w(spec fixtures) << fixture_name)
14
+ file if File.file?(file) or raise "Not found fixture in #{file}"
15
+ end
16
+
17
+ end
@@ -0,0 +1,39 @@
1
+ module Ginst::Test
2
+
3
+ # Do something in a directory
4
+ def in_dir(dir)
5
+ old_dir = File.expand_path(Dir.pwd)
6
+ Dir.chdir dir
7
+ yield dir
8
+ ensure
9
+ Dir.chdir old_dir
10
+ end
11
+
12
+
13
+ # Return a temp dir inside ginst temp dir that exists
14
+ def create_temp_dir
15
+ dir = temp_dir
16
+ FileUtils.mkdir_p(dir)
17
+ dir
18
+ end
19
+
20
+ # Return a temporal directory inside ginst temp dir and it may not exists
21
+ def temp_dir
22
+ begin dir = File.join(ginst_temp_dir, (i ||= 0; i += 1).to_s)
23
+ end while(File.exists?(dir))
24
+ dir
25
+ end
26
+
27
+ # Remove ginst temp dir
28
+ def remove_temp_dir
29
+ FileUtils.rm_rf(ginst_temp_dir)
30
+ ginst_temp_dir
31
+ end
32
+
33
+ # Return ginst tempory directory
34
+ def ginst_temp_dir
35
+ dir = File.join(Dir.tmpdir,'ginst')
36
+ dir
37
+ end
38
+
39
+ end