ginst 0.1.3 → 0.2.2

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.
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