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.
- data.tar.gz.sig +0 -0
- data/Manifest +34 -8
- data/Rakefile +6 -3
- data/bin/ginst +11 -4
- data/features/browsing_project.feature +20 -0
- data/features/manage_projects.feature +36 -0
- data/features/manage_server.feature +17 -0
- data/features/step_definitions/manage_projects_steps.rb +35 -0
- data/features/step_definitions/manage_server_steps.rb +33 -0
- data/features/step_definitions/webrat_steps.rb +99 -0
- data/features/support/env.rb +29 -0
- data/features/support/git.rb +22 -0
- data/features/support/paths.rb +16 -0
- data/ginst.gemspec +8 -11
- data/lib/app/authorization.rb +1 -1
- data/lib/app/views/_commit.haml +8 -0
- data/lib/app/views/commit.haml +38 -8
- data/lib/app/views/commits.haml +1 -1
- data/lib/app/views/edit.haml +11 -0
- data/lib/app/views/layout.haml +10 -7
- data/lib/app/views/new.haml +19 -0
- data/lib/app/views/project.haml +4 -1
- data/lib/app/views/ref.haml +3 -0
- data/lib/app/views/style.sass +0 -1
- data/lib/app/webserver.rb +66 -76
- data/lib/ginst.rb +7 -5
- data/lib/ginst/core_extensions.rb +32 -0
- data/lib/ginst/ginst.rb +5 -72
- data/lib/ginst/project.rb +13 -38
- data/lib/ginst/project/base.rb +59 -0
- data/lib/ginst/project/build.rb +117 -0
- data/lib/ginst/project/commit_db.rb +29 -0
- data/lib/ginst/project/finders.rb +17 -0
- data/lib/ginst/project/grit.rb +53 -0
- data/lib/ginst/project/validations.rb +60 -0
- data/lib/ginst/test.rb +8 -0
- data/lib/ginst/test/base.rb +17 -0
- data/lib/ginst/test/dir.rb +39 -0
- data/lib/ginst/test/repo.rb +13 -0
- data/lib/ginst/test/run.rb +11 -0
- data/lib/ginst/web_server.rb +43 -0
- data/spec/fixtures/sample_repo.zip +0 -0
- data/spec/models/project_base_spec.rb +56 -0
- data/spec/models/project_build_spec.rb +18 -0
- data/spec/models/project_commit_db_spec.rb +19 -0
- data/spec/models/project_finders_spec.rb +34 -0
- data/spec/models/project_grit_spec.rb +19 -0
- data/spec/models/project_validations_spec.rb +72 -0
- data/spec/spec_helper.rb +8 -0
- metadata +56 -29
- metadata.gz.sig +0 -0
- data/lib/app/views/full_commit.haml +0 -38
- data/lib/app/views/project_index.haml +0 -11
- data/lib/ginst/command_line.rb +0 -47
- data/lib/ginst/master_command_line.rb +0 -40
- data/lib/ginst/plugin.rb +0 -16
- data/lib/ginst/server.rb +0 -51
- data/lib/ginst/templates/rgithook.rb +0 -6
data/lib/ginst/project.rb
CHANGED
@@ -1,40 +1,15 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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,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
|