ciquantum 0.0.3 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -9
- data/bin/ciquantum +1 -1
- data/ciquantum.gemspec +2 -0
- data/examples/{cijoe.ru → ciquantum.ru} +0 -0
- data/examples/{cijoed → ciquantumd} +0 -0
- data/lib/ciquantum.rb +6 -218
- data/lib/ciquantum/build.rb +4 -3
- data/lib/ciquantum/commit.rb +2 -2
- data/lib/ciquantum/config.rb +1 -1
- data/lib/ciquantum/core.rb +268 -0
- data/lib/ciquantum/queue.rb +1 -14
- data/lib/ciquantum/server.rb +57 -29
- data/lib/ciquantum/{unfuddle_adapter.rb → unfuddle.rb} +5 -5
- data/lib/ciquantum/unfuddle/changeset.rb +44 -0
- data/lib/ciquantum/version.rb +2 -2
- data/lib/ciquantum/views/template.erb +10 -29
- data/sample.rb +15 -0
- data/test/test_hooks.rb +13 -11
- metadata +40 -8
- data/lib/ciquantum/adapter.rb +0 -15
- data/lib/ciquantum/public/favicon.ico +0 -0
- data/lib/ciquantum/public/octocat.png +0 -0
data/README.md
CHANGED
@@ -24,12 +24,12 @@ At current moment gem uses Unfuddle adapter, and must be configured for proper w
|
|
24
24
|
Expected options:
|
25
25
|
-----------------
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
git config --add ciquantum.runner "command to do on build"
|
28
|
+
git config --add ciquantum.branch branch_name
|
29
|
+
git config --add ciquantum.buildqueue true
|
30
|
+
git config --add ciquantum.projectname project_name_to_show
|
31
|
+
git config --add ciquantum.coverage_path relative_path_to_coverage_report
|
32
|
+
|
33
|
+
Unfuddle (default) adapter:
|
34
|
+
git config --add ciquantum.unfuddle.project unfuddle_user_name
|
35
|
+
git config --add ciquantum.unfuddle.user unfuddle_project_id
|
data/bin/ciquantum
CHANGED
data/ciquantum.gemspec
CHANGED
@@ -24,6 +24,8 @@ Gem::Specification.new do |gem|
|
|
24
24
|
gem.add_runtime_dependency 'tinder', '>= 1.4.0'
|
25
25
|
gem.add_runtime_dependency 'sinatra'
|
26
26
|
gem.add_runtime_dependency 'choice'
|
27
|
+
gem.add_runtime_dependency 'pony'
|
28
|
+
gem.add_runtime_dependency 'sq_auth', '>= 0.0.21'
|
27
29
|
gem.add_development_dependency 'rack-test'
|
28
30
|
gem.add_development_dependency 'mocha'
|
29
31
|
|
File without changes
|
File without changes
|
data/lib/ciquantum.rb
CHANGED
@@ -1,228 +1,16 @@
|
|
1
1
|
|
2
2
|
require 'ciquantum/version'
|
3
3
|
require 'ciquantum/config'
|
4
|
-
require 'ciquantum/
|
4
|
+
require 'ciquantum/unfuddle'
|
5
|
+
require 'ciquantum/unfuddle/changeset'
|
5
6
|
require 'ciquantum/commit'
|
6
7
|
require 'ciquantum/build'
|
7
8
|
require 'ciquantum/server'
|
8
9
|
require 'ciquantum/queue'
|
10
|
+
require 'ciquantum/core'
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
attr_reader :project
|
14
|
-
attr_reader :url
|
15
|
-
attr_reader :current_build
|
16
|
-
attr_reader :last_build
|
17
|
-
attr_reader :campfire
|
18
|
-
attr_reader :projectname
|
19
|
-
attr_reader :coverage_path
|
20
|
-
|
21
|
-
def initialize(project_path)
|
22
|
-
@project_path = File.expand_path(project_path)
|
23
|
-
|
24
|
-
@user, @project = git_user_and_project
|
25
|
-
@url = project_url
|
26
|
-
@projectname = repo_config.projectname.to_s
|
27
|
-
@coverage_path = repo_config.coveragepath.to_s
|
28
|
-
|
29
|
-
@last_build = nil
|
30
|
-
@current_build = nil
|
31
|
-
@queue = Queue.new(!repo_config.buildqueue.to_s.empty?, true)
|
32
|
-
|
33
|
-
trap("INT") { stop }
|
34
|
-
end
|
35
|
-
|
36
|
-
# is a build running?
|
37
|
-
def building?
|
38
|
-
!!@current_build
|
39
|
-
end
|
40
|
-
|
41
|
-
# the pid of the running child process
|
42
|
-
def pid
|
43
|
-
building? and current_build.pid
|
44
|
-
end
|
45
|
-
|
46
|
-
# kill the child and exit
|
47
|
-
def stop
|
48
|
-
Process.kill(9, pid) if pid
|
49
|
-
exit!
|
50
|
-
end
|
51
|
-
|
52
|
-
# build callbacks
|
53
|
-
def build_failed(output, error)
|
54
|
-
finish_build :failed, "#{error}\n\n#{output}"
|
55
|
-
run_hook "build-failed"
|
56
|
-
end
|
57
|
-
|
58
|
-
def build_worked(output)
|
59
|
-
finish_build :worked, output
|
60
|
-
run_hook "build-worked"
|
61
|
-
end
|
62
|
-
|
63
|
-
def finish_build(status, output)
|
64
|
-
@current_build.finished_at = Time.now
|
65
|
-
@current_build.status = status
|
66
|
-
@current_build.output = output
|
67
|
-
@last_build = @current_build
|
68
|
-
|
69
|
-
@current_build = nil
|
70
|
-
write_build 'current', @current_build
|
71
|
-
write_build 'last', @last_build
|
72
|
-
|
73
|
-
build(@queue.next_branch_to_build) if @queue.waiting?
|
74
|
-
end
|
75
|
-
|
76
|
-
# run the build but make sure only one is running
|
77
|
-
# at a time (if new one comes in we will park it)
|
78
|
-
def build(branch=nil)
|
79
|
-
if building?
|
80
|
-
@queue.append_unless_already_exists(branch)
|
81
|
-
# leave anyway because a current build runs
|
82
|
-
return
|
83
|
-
end
|
84
|
-
@current_build = Build.new(@project_path, @user, @project)
|
85
|
-
write_build 'current', @current_build
|
86
|
-
Thread.new { build!(branch) }
|
87
|
-
end
|
88
|
-
|
89
|
-
def open_pipe(cmd)
|
90
|
-
read, write = IO.pipe
|
91
|
-
|
92
|
-
pid = fork do
|
93
|
-
read.close
|
94
|
-
$stdout.reopen write
|
95
|
-
exec cmd
|
96
|
-
end
|
97
|
-
|
98
|
-
write.close
|
99
|
-
|
100
|
-
yield read, pid
|
101
|
-
end
|
102
|
-
|
103
|
-
# update git then run the build
|
104
|
-
def build!(branch=nil)
|
105
|
-
@git_branch = branch
|
106
|
-
build = @current_build
|
107
|
-
output = ''
|
108
|
-
git_update
|
109
|
-
build.sha = git_sha
|
110
|
-
build.branch = git_branch
|
111
|
-
write_build 'current', build
|
112
|
-
|
113
|
-
open_pipe("cd #{@project_path} && #{runner_command} 2>&1") do |pipe, pid|
|
114
|
-
puts "#{Time.now.to_i}: Building #{build.branch} at #{build.short_sha}: pid=#{pid}"
|
115
|
-
|
116
|
-
build.pid = pid
|
117
|
-
write_build 'current', build
|
118
|
-
output = pipe.read
|
119
|
-
end
|
120
|
-
|
121
|
-
Process.waitpid(build.pid, 1)
|
122
|
-
status = $?.exitstatus.to_i
|
123
|
-
@current_build = build
|
124
|
-
puts "#{Time.now.to_i}: Built #{build.short_sha}: status=#{status}"
|
125
|
-
|
126
|
-
status == 0 ? build_worked(output) : build_failed('', output)
|
127
|
-
rescue Object => e
|
128
|
-
puts "Exception building: #{e.message} (#{e.class})"
|
129
|
-
build_failed('', e.to_s)
|
130
|
-
end
|
131
|
-
|
132
|
-
# shellin' out
|
133
|
-
def runner_command
|
134
|
-
runner = repo_config.runner.to_s
|
135
|
-
runner == '' ? "rake -s test:units" : runner
|
136
|
-
end
|
137
|
-
|
138
|
-
def git_sha
|
139
|
-
`cd #{@project_path} && git rev-parse origin/#{git_branch}`.chomp
|
140
|
-
end
|
141
|
-
|
142
|
-
def git_update
|
143
|
-
`cd #{@project_path} && git fetch origin && git reset --hard origin/#{git_branch}`
|
144
|
-
run_hook "after-reset"
|
145
|
-
end
|
146
|
-
|
147
|
-
def git_user_and_project
|
148
|
-
adapter_config = repo_config.send Adapter.default_adapter.name
|
149
|
-
Adapter.default_adapter.git_user_and_project adapter_config
|
150
|
-
end
|
151
|
-
|
152
|
-
def project_url
|
153
|
-
Adapter.default_adapter.project_url @user, @project
|
154
|
-
end
|
155
|
-
|
156
|
-
def git_branches
|
157
|
-
branches = `cd #{@project_path} && git branch -r`.split("\n")
|
158
|
-
branches.collect{|s| s =~ /(?:\s|^)origin\/(?!HEAD)(\w+)(?:\s|$)/ ? $1.strip : nil}.compact
|
159
|
-
end
|
160
|
-
|
161
|
-
def git_branch
|
162
|
-
return @git_branch if @git_branch
|
163
|
-
branch = repo_config.branch.to_s
|
164
|
-
@git_branch = branch == '' ? "master" : branch
|
165
|
-
end
|
166
|
-
|
167
|
-
# massage our repo
|
168
|
-
def run_hook(hook)
|
169
|
-
if File.exists?(file=path_in_project(".git/hooks/#{hook}")) && File.executable?(file)
|
170
|
-
data =
|
171
|
-
if @last_build && @last_build.commit
|
172
|
-
{
|
173
|
-
"MESSAGE" => @last_build.commit.message,
|
174
|
-
"AUTHOR" => @last_build.commit.author,
|
175
|
-
"SHA" => @last_build.commit.sha,
|
176
|
-
"OUTPUT" => @last_build.env_output
|
177
|
-
}
|
178
|
-
else
|
179
|
-
{}
|
180
|
-
end
|
181
|
-
|
182
|
-
orig_ENV = ENV.to_hash
|
183
|
-
ENV.clear
|
184
|
-
data.each{ |k, v| ENV[k] = v }
|
185
|
-
output = `cd #{@project_path} && #{file}`
|
186
|
-
|
187
|
-
ENV.clear
|
188
|
-
orig_ENV.to_hash.each{ |k, v| ENV[k] = v}
|
189
|
-
output
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# restore current / last build state from disk.
|
194
|
-
def restore
|
195
|
-
@last_build = read_build('last')
|
196
|
-
@current_build = read_build('current')
|
197
|
-
|
198
|
-
Process.kill(0, @current_build.pid) if @current_build && @current_build.pid
|
199
|
-
rescue Errno::ESRCH
|
200
|
-
# build pid isn't running anymore. assume previous
|
201
|
-
# server died and reset.
|
202
|
-
@current_build = nil
|
203
|
-
end
|
204
|
-
|
205
|
-
def path_in_project(path)
|
206
|
-
File.join(@project_path, path)
|
207
|
-
end
|
208
|
-
|
209
|
-
# write build info for build to file.
|
210
|
-
def write_build(name, build)
|
211
|
-
filename = path_in_project(".git/builds/#{name}")
|
212
|
-
Dir.mkdir path_in_project('.git/builds') unless File.directory?(path_in_project('.git/builds'))
|
213
|
-
if build
|
214
|
-
build.dump filename
|
215
|
-
elsif File.exist?(filename)
|
216
|
-
File.unlink filename
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def repo_config
|
221
|
-
Config.ciquantum(@project_path)
|
222
|
-
end
|
223
|
-
|
224
|
-
# load build info from file.
|
225
|
-
def read_build(name)
|
226
|
-
Build.load(path_in_project(".git/builds/#{name}"), @project_path)
|
12
|
+
module CIQuantum
|
13
|
+
def self.new *args
|
14
|
+
CIQuantum::Core.new *args
|
227
15
|
end
|
228
16
|
end
|
data/lib/ciquantum/build.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
|
-
|
3
|
+
module CIQuantum
|
4
4
|
class Build < Struct.new(:project_path, :user, :project, :started_at, :finished_at, :sha, :status, :output, :pid, :branch)
|
5
5
|
def initialize(*args)
|
6
6
|
super
|
@@ -51,13 +51,14 @@ class CIQuantum
|
|
51
51
|
@commit ||= Commit.new(sha, user, project, project_path)
|
52
52
|
end
|
53
53
|
|
54
|
-
def dump
|
54
|
+
def dump file
|
55
55
|
config = [user, project, started_at, finished_at, sha, status, output, pid, branch]
|
56
56
|
data = YAML.dump(config)
|
57
57
|
File.open(file, 'wb') { |io| io.write(data) }
|
58
58
|
end
|
59
59
|
|
60
|
-
def self.load
|
60
|
+
def self.load file, project_path
|
61
|
+
puts "Loading build from file: #{file}"
|
61
62
|
if File.exist?(file)
|
62
63
|
config = YAML.load(File.read(file)).unshift(project_path)
|
63
64
|
new *config
|
data/lib/ciquantum/commit.rb
CHANGED
data/lib/ciquantum/config.rb
CHANGED
@@ -0,0 +1,268 @@
|
|
1
|
+
module CIQuantum
|
2
|
+
class Core
|
3
|
+
attr_reader :user
|
4
|
+
attr_reader :project
|
5
|
+
attr_reader :url
|
6
|
+
attr_reader :current_build
|
7
|
+
attr_reader :last_build
|
8
|
+
attr_reader :campfire
|
9
|
+
attr_reader :projectname
|
10
|
+
attr_reader :coverage_path
|
11
|
+
|
12
|
+
HistoryLimit = 10
|
13
|
+
|
14
|
+
def initialize(project_path)
|
15
|
+
@project_path = File.expand_path(project_path)
|
16
|
+
|
17
|
+
@user, @project = git_user_and_project
|
18
|
+
@url = project_url
|
19
|
+
@projectname = repo_config.projectname.to_s
|
20
|
+
@coverage_path = repo_config.coveragepath.to_s
|
21
|
+
|
22
|
+
@last_build = nil
|
23
|
+
@current_build = nil
|
24
|
+
@queue = Queue.new(!repo_config.buildqueue.to_s.empty?, true)
|
25
|
+
|
26
|
+
trap("INT") { stop }
|
27
|
+
end
|
28
|
+
|
29
|
+
# is a build running?
|
30
|
+
def building?
|
31
|
+
!!@current_build
|
32
|
+
end
|
33
|
+
|
34
|
+
# the pid of the running child process
|
35
|
+
def pid
|
36
|
+
building? and current_build.pid
|
37
|
+
end
|
38
|
+
|
39
|
+
# kill the child and exit
|
40
|
+
def stop
|
41
|
+
Process.kill(9, pid) if pid
|
42
|
+
exit!
|
43
|
+
end
|
44
|
+
|
45
|
+
# build callbacks
|
46
|
+
def build_failed(output, error)
|
47
|
+
finish_build :failed, "#{error}\n\n#{output}"
|
48
|
+
run_hook "build-failed"
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_worked(output)
|
52
|
+
finish_build :worked, output
|
53
|
+
run_hook "build-worked"
|
54
|
+
end
|
55
|
+
|
56
|
+
def finish_build(status, output)
|
57
|
+
@current_build.finished_at = Time.now
|
58
|
+
@current_build.status = status
|
59
|
+
@current_build.output = output
|
60
|
+
@last_build = @current_build
|
61
|
+
|
62
|
+
@current_build = nil
|
63
|
+
write_current_build
|
64
|
+
write_last_build
|
65
|
+
run_hook "postbuild"
|
66
|
+
|
67
|
+
build(@queue.next_branch_to_build) if @queue.waiting?
|
68
|
+
end
|
69
|
+
|
70
|
+
def build(branch=nil)
|
71
|
+
if building?
|
72
|
+
@queue.append_unless_already_exists(branch)
|
73
|
+
# leave anyway because a current build runs
|
74
|
+
return
|
75
|
+
end
|
76
|
+
@current_build = Build.new(@project_path, @user, @project)
|
77
|
+
write_current_build
|
78
|
+
|
79
|
+
Thread.new {
|
80
|
+
build!(branch)
|
81
|
+
}
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
def open_pipe(cmd)
|
86
|
+
read, write = IO.pipe
|
87
|
+
|
88
|
+
pid = fork do
|
89
|
+
read.close
|
90
|
+
$stdout.reopen write
|
91
|
+
exec cmd
|
92
|
+
end
|
93
|
+
|
94
|
+
write.close
|
95
|
+
|
96
|
+
yield read, pid
|
97
|
+
end
|
98
|
+
|
99
|
+
# update git then run the build
|
100
|
+
def build!(branch=nil)
|
101
|
+
@git_branch = branch
|
102
|
+
build = @current_build
|
103
|
+
run_hook "prebuild"
|
104
|
+
git_update
|
105
|
+
output = ''
|
106
|
+
build.sha = git_sha
|
107
|
+
build.branch = git_branch
|
108
|
+
write_build 'current', build
|
109
|
+
|
110
|
+
open_pipe("cd #{@project_path} && #{runner_command} 2>&1") do |pipe, pid|
|
111
|
+
puts "#{Time.now.to_i}: Building #{build.branch} at #{build.short_sha}: pid=#{pid}"
|
112
|
+
|
113
|
+
build.pid = pid
|
114
|
+
write_build 'current', build
|
115
|
+
output = pipe.read
|
116
|
+
end
|
117
|
+
|
118
|
+
Process.waitpid(build.pid, 1)
|
119
|
+
status = $?.exitstatus.to_i
|
120
|
+
@current_build = build
|
121
|
+
puts "#{Time.now.to_i}: Built #{build.short_sha}: status=#{status}"
|
122
|
+
|
123
|
+
status == 0 ? build_worked(output) : build_failed('', output)
|
124
|
+
rescue Object => e
|
125
|
+
puts "Exception building: #{e.message} (#{e.class})"
|
126
|
+
build_failed('', e.to_s)
|
127
|
+
end
|
128
|
+
|
129
|
+
# shellin' out
|
130
|
+
def runner_command
|
131
|
+
runner = repo_config.runner.to_s
|
132
|
+
runner == '' ? "rake -s test:units" : runner
|
133
|
+
end
|
134
|
+
|
135
|
+
def git_update
|
136
|
+
`cd #{@project_path} && git fetch origin && git reset --hard origin/#{git_branch}`
|
137
|
+
run_hook "after-reset"
|
138
|
+
end
|
139
|
+
|
140
|
+
def git_user_and_project
|
141
|
+
config = repo_config.send Unfuddle.name
|
142
|
+
Unfuddle.git_user_and_project config
|
143
|
+
end
|
144
|
+
|
145
|
+
def project_url
|
146
|
+
Unfuddle.project_url @user, @project
|
147
|
+
end
|
148
|
+
|
149
|
+
# massage our repo
|
150
|
+
def run_hook(hook)
|
151
|
+
if File.exists?(file=path_in_project(".git/hooks/#{hook}")) && File.executable?(file)
|
152
|
+
data =
|
153
|
+
if @last_build && @last_build.commit
|
154
|
+
{
|
155
|
+
"MESSAGE" => @last_build.commit.message,
|
156
|
+
"AUTHOR" => @last_build.commit.author,
|
157
|
+
"SHA" => @last_build.commit.sha,
|
158
|
+
"URL" => @last_build.commit.url,
|
159
|
+
"OUTPUT" => @last_build.env_output
|
160
|
+
}
|
161
|
+
else
|
162
|
+
{}
|
163
|
+
end
|
164
|
+
|
165
|
+
orig_ENV = ENV.to_hash
|
166
|
+
# ENV.clear
|
167
|
+
data.each{ |k, v| ENV[k] = v }
|
168
|
+
puts "Executing hook: #{file}"
|
169
|
+
output = `cd #{@project_path} && #{file}`
|
170
|
+
|
171
|
+
ENV.clear
|
172
|
+
orig_ENV.to_hash.each{ |k, v| ENV[k] = v}
|
173
|
+
output
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# restore current / last build state from disk.
|
178
|
+
def restore
|
179
|
+
@last_build = read_last_build
|
180
|
+
@current_build = read_current_build
|
181
|
+
|
182
|
+
Process.kill(0, @current_build.pid) if @current_build && @current_build.pid
|
183
|
+
rescue Errno::ESRCH
|
184
|
+
@current_build = nil
|
185
|
+
end
|
186
|
+
|
187
|
+
def path_in_project(path)
|
188
|
+
File.join(@project_path, path)
|
189
|
+
end
|
190
|
+
|
191
|
+
def repo_config
|
192
|
+
Config.ciquantum(@project_path)
|
193
|
+
end
|
194
|
+
|
195
|
+
def swith_branch branch
|
196
|
+
repo_config.branch.set branch if git_branches.include? branch
|
197
|
+
end
|
198
|
+
|
199
|
+
def git_branches
|
200
|
+
branches = `cd #{@project_path} && git branch -r`.split("\n")
|
201
|
+
branches.collect{|s| s =~ /(?:\s|^)origin\/(?!HEAD)(\w+)(?:\s|$)/ ? $1.strip : nil}.compact
|
202
|
+
end
|
203
|
+
|
204
|
+
def git_branch
|
205
|
+
return @git_branch if @git_branch
|
206
|
+
branch = repo_config.branch.to_s
|
207
|
+
@git_branch = (branch == '' ? "master" : branch)
|
208
|
+
end
|
209
|
+
|
210
|
+
def git_sha
|
211
|
+
`cd #{@project_path} && git rev-parse origin/#{git_branch}`.chomp
|
212
|
+
end
|
213
|
+
|
214
|
+
def read_build(name)
|
215
|
+
Build.load(path_in_project(".git/builds/#{name}"), @project_path)
|
216
|
+
end
|
217
|
+
|
218
|
+
def read_last_build
|
219
|
+
read_build_by_index 0
|
220
|
+
end
|
221
|
+
|
222
|
+
def read_current_build
|
223
|
+
read_build 'current'
|
224
|
+
end
|
225
|
+
|
226
|
+
def read_build_by_index index
|
227
|
+
read_build old_builds[index] if old_builds[index]
|
228
|
+
end
|
229
|
+
|
230
|
+
def old_builds
|
231
|
+
Dir.glob(path_in_project(".git/builds/*")).collect do |f|
|
232
|
+
File.basename(f)
|
233
|
+
end.select do |f|
|
234
|
+
f =~ /\d+/
|
235
|
+
end.sort.reverse
|
236
|
+
end
|
237
|
+
|
238
|
+
def write_last_build
|
239
|
+
write_build @last_build.finished_at.to_i.to_s, @last_build
|
240
|
+
end
|
241
|
+
|
242
|
+
def write_current_build
|
243
|
+
write_build 'current', @current_build
|
244
|
+
end
|
245
|
+
|
246
|
+
def write_build(name, build)
|
247
|
+
filename = path_in_project(".git/builds/#{name}")
|
248
|
+
Dir.mkdir path_in_project('.git/builds') unless File.directory?(path_in_project('.git/builds'))
|
249
|
+
if build
|
250
|
+
build.dump filename
|
251
|
+
elsif File.exist?(filename)
|
252
|
+
File.unlink filename
|
253
|
+
end
|
254
|
+
remove_unused_build
|
255
|
+
end
|
256
|
+
|
257
|
+
def remove_unused_build
|
258
|
+
builds = old_builds
|
259
|
+
if builds.size > HistoryLimit
|
260
|
+
builds[HistoryLimit..builds.size].each do |file|
|
261
|
+
file = path_in_project(".git/builds/#{file}")
|
262
|
+
File.delete file if File.exists? file
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
end
|
data/lib/ciquantum/queue.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
|
2
|
-
# An in memory queue used for maintaining an order list of requested
|
3
|
-
# builds.
|
1
|
+
module CIQuantum
|
4
2
|
class Queue
|
5
|
-
# enabled - determines whether builds should be queued or not.
|
6
3
|
def initialize(enabled, verbose=false)
|
7
4
|
@enabled = enabled
|
8
5
|
@verbose = verbose
|
@@ -11,13 +8,6 @@ class CIQuantum
|
|
11
8
|
log("Build queueing enabled") if enabled
|
12
9
|
end
|
13
10
|
|
14
|
-
# Public: Appends a branch to be built, unless it already exists
|
15
|
-
# within the queue.
|
16
|
-
#
|
17
|
-
# branch - the name of the branch to build or nil if the default
|
18
|
-
# should be built.
|
19
|
-
#
|
20
|
-
# Returns nothing
|
21
11
|
def append_unless_already_exists(branch)
|
22
12
|
return unless enabled?
|
23
13
|
unless @queue.include? branch
|
@@ -26,15 +16,12 @@ class CIQuantum
|
|
26
16
|
end
|
27
17
|
end
|
28
18
|
|
29
|
-
# Returns a String of the next branch to build
|
30
19
|
def next_branch_to_build
|
31
20
|
branch = @queue.shift
|
32
21
|
log "#{Time.now.to_i}: De-queueing #{branch}"
|
33
22
|
branch
|
34
23
|
end
|
35
24
|
|
36
|
-
# Returns true if there are requested builds waiting and false
|
37
|
-
# otherwise.
|
38
25
|
def waiting?
|
39
26
|
if enabled?
|
40
27
|
not @queue.empty?
|
data/lib/ciquantum/server.rb
CHANGED
@@ -1,9 +1,16 @@
|
|
1
|
-
require 'sinatra/base'
|
2
1
|
require 'erb'
|
3
2
|
require 'json'
|
4
3
|
|
5
|
-
|
4
|
+
require 'sinatra'
|
5
|
+
require 'sq_auth'
|
6
|
+
|
7
|
+
module CIQuantum
|
6
8
|
class Server < Sinatra::Base
|
9
|
+
|
10
|
+
SqAuth.connect(host: "sqauth.socialquantum.com", port: 443) do |config|
|
11
|
+
config.project_name = "Enchanted"
|
12
|
+
end
|
13
|
+
|
7
14
|
attr_reader :quantum
|
8
15
|
|
9
16
|
dir = File.dirname(File.expand_path(__FILE__))
|
@@ -13,6 +20,9 @@ class CIQuantum
|
|
13
20
|
set :static, true
|
14
21
|
set :lock, true
|
15
22
|
|
23
|
+
enable :static
|
24
|
+
enable :sessions
|
25
|
+
|
16
26
|
before { quantum.restore }
|
17
27
|
|
18
28
|
get '/ping' do
|
@@ -23,33 +33,35 @@ class CIQuantum
|
|
23
33
|
quantum.last_build.sha
|
24
34
|
end
|
25
35
|
|
26
|
-
get '/?' do
|
27
|
-
erb(:template, {}, :quantum => quantum)
|
36
|
+
get ["ci::view", "ci::change_branch"], '/?' do
|
37
|
+
erb(:template, {}, :quantum => quantum, :can_change_branch => accessed_by?("ci::change_branch"))
|
28
38
|
end
|
29
39
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
payload = JSON.parse(params[:payload])
|
34
|
-
pushed_branch = payload["ref"].split('/').last
|
40
|
+
sq_auth_access do
|
41
|
+
access_action "/build" do
|
42
|
+
execute_for "ci::view"
|
35
43
|
end
|
36
|
-
|
37
|
-
|
38
|
-
# or the payload exists and the "ref" property matches our
|
39
|
-
# specified build branch.
|
40
|
-
if params[:branch] || params[:rebuild] || pushed_branch == quantum.git_branch
|
41
|
-
quantum.build(params[:branch])
|
44
|
+
access_action "/switch_branch" do
|
45
|
+
execute_for "ci::admin"
|
42
46
|
end
|
47
|
+
end
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
post '/build' do
|
50
|
+
quantum.build quantum.repo_config.branch
|
51
|
+
|
52
|
+
link_coverage_path
|
53
|
+
redirect '/'
|
54
|
+
end
|
47
55
|
|
56
|
+
post '/switch_branch' do
|
57
|
+
quantum.swith_branch params[:branch]
|
58
|
+
quantum.build params[:branch]
|
48
59
|
|
49
|
-
|
60
|
+
link_coverage_path
|
61
|
+
redirect '/'
|
50
62
|
end
|
51
63
|
|
52
|
-
get '/api/json' do
|
64
|
+
get ["ci::view"], '/api/json' do
|
53
65
|
response = [200, {'Content-Type' => 'application/json'}]
|
54
66
|
response_json = erb(:json, {}, :quantum => quantum)
|
55
67
|
if params[:jsonp]
|
@@ -60,8 +72,20 @@ class CIQuantum
|
|
60
72
|
response
|
61
73
|
end
|
62
74
|
|
63
|
-
|
64
|
-
|
75
|
+
post '/push' do
|
76
|
+
begin
|
77
|
+
xml = request.body.read
|
78
|
+
changeset = Unfuddle::Changeset.new(xml)
|
79
|
+
quantum.build quantum.repo_config.branch
|
80
|
+
link_coverage_path
|
81
|
+
rescue Unfuddle::ChangesetError => e
|
82
|
+
logger.error "[error] Changeset error: #{e.inspect}, Content: #{xml.inspect}"
|
83
|
+
halt 400, "Changeset Error: #{e.message}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
get ["ci::view"], '/coverage?' do
|
88
|
+
redirect "coverage/index.html"
|
65
89
|
end
|
66
90
|
|
67
91
|
helpers do
|
@@ -83,6 +107,7 @@ class CIQuantum
|
|
83
107
|
root = "" if root == "/"
|
84
108
|
root
|
85
109
|
end
|
110
|
+
|
86
111
|
end
|
87
112
|
|
88
113
|
def initialize(*args)
|
@@ -101,14 +126,17 @@ class CIQuantum
|
|
101
126
|
self.new
|
102
127
|
end
|
103
128
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
129
|
+
def link_coverage_path
|
130
|
+
public_coverage_path = File.join settings.public_folder, "coverage"
|
131
|
+
project_coverage_path = File.join settings.project_path, quantum.coverage_path
|
132
|
+
|
133
|
+
File.unlink public_coverage_path if File.exists? public_coverage_path
|
134
|
+
if !quantum.coverage_path.empty? and File.exists? project_coverage_path
|
135
|
+
File.symlink project_coverage_path, public_coverage_path
|
111
136
|
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.project_path=(project_path)
|
112
140
|
set :project_path, Proc.new{project_path}, true
|
113
141
|
end
|
114
142
|
|
@@ -1,5 +1,5 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module CIQuantum
|
2
|
+
module Unfuddle
|
3
3
|
|
4
4
|
def self.name
|
5
5
|
"unfuddle"
|
@@ -10,12 +10,12 @@ class CIQuantum
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.git_user_and_project config
|
13
|
-
|
13
|
+
[ config.user, config.project ]
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.project_url user, project
|
17
|
-
|
17
|
+
"https://#{user}.unfuddle.com/a#/repositories/#{project}/browse"
|
18
18
|
end
|
19
19
|
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module CIQuantum
|
4
|
+
module Unfuddle
|
5
|
+
class ChangesetError < StandardError ; end
|
6
|
+
|
7
|
+
class Changeset
|
8
|
+
FIELDS = %w(repository_id revision message committer_name committer_date).freeze
|
9
|
+
|
10
|
+
attr_reader :author
|
11
|
+
attr_reader :message
|
12
|
+
attr_reader :date
|
13
|
+
attr_reader :commit
|
14
|
+
attr_reader :repo
|
15
|
+
attr_reader :xml
|
16
|
+
|
17
|
+
def initialize(xml)
|
18
|
+
xml = xml.to_s.strip
|
19
|
+
raise ChangesetError, 'Changeset XML required!' if xml.empty?
|
20
|
+
|
21
|
+
begin
|
22
|
+
@data = Hash.from_xml(xml)
|
23
|
+
rescue REXML::ParseException
|
24
|
+
raise ChangesetError, 'Invalid XML data!'
|
25
|
+
end
|
26
|
+
|
27
|
+
raise ChangesetError, 'Invalid changeset!' unless @data.key?('changeset')
|
28
|
+
|
29
|
+
@xml = xml
|
30
|
+
@data = @data['changeset']
|
31
|
+
|
32
|
+
unless (FIELDS & @data.keys).size == FIELDS.size
|
33
|
+
raise ChangesetError, 'Invalid changeset!'
|
34
|
+
end
|
35
|
+
|
36
|
+
@commit = @data['revision']
|
37
|
+
@author = @data['committer_name']
|
38
|
+
@message = @data['message']
|
39
|
+
@date = @data['committer_date']
|
40
|
+
@repo = @data['repository_id']
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/ciquantum/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
Version = VERSION = "0.0.
|
1
|
+
module CIQuantum
|
2
|
+
Version = VERSION = "0.0.6"
|
3
3
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
<html>
|
3
3
|
<head>
|
4
4
|
<link href="<%= ciquantum_root %>/screen.css" media="screen" rel="stylesheet" type="text/css" />
|
5
|
-
<link rel="shortcut icon" href="<%= ciquantum_root %>/favicon.ico" type="image/x-icon" />
|
6
5
|
<title><%= h(quantum.projectname) %>: CI Quantum</title>
|
7
6
|
</head>
|
8
7
|
<body>
|
@@ -26,16 +25,22 @@
|
|
26
25
|
</li>
|
27
26
|
<% else %>
|
28
27
|
<li>
|
29
|
-
<form method="POST">
|
30
|
-
<
|
28
|
+
<form method="POST" action="/build">
|
29
|
+
<span class="branch">Current branch: <%= quantum.git_branch %></span>
|
30
|
+
<input type="submit" name="switch" value="Build"/>
|
31
|
+
</form>
|
32
|
+
</li>
|
33
|
+
<li>
|
34
|
+
<% if can_change_branch %>
|
35
|
+
<form method="POST" action="/switch_branch">
|
31
36
|
<select name="branch">
|
32
37
|
<% for @branch in quantum.git_branches %>
|
33
38
|
<option value="<%= @branch %>" <%= "selected=\"selected\"" if (@branch == quantum.git_branch) %> >"<%= @branch %>"</option>
|
34
39
|
<% end %>
|
35
40
|
</select>
|
36
|
-
<input type="submit" name="switch" value="
|
37
|
-
<input type="submit" name="switch" value="Switch branch"/>
|
41
|
+
<input type="submit" name="switch" value="Switch branch and build"/>
|
38
42
|
</form>
|
43
|
+
<% end %>
|
39
44
|
</li>
|
40
45
|
<% end %>
|
41
46
|
|
@@ -61,30 +66,6 @@
|
|
61
66
|
<% end %>
|
62
67
|
</ul>
|
63
68
|
</div>
|
64
|
-
|
65
|
-
<div class="footer">
|
66
|
-
<div class="contact">
|
67
|
-
<p>
|
68
|
-
<a href="https://github.com/meredian/ciquantum#readme">Documentation</a><br/>
|
69
|
-
<a href="https://github.com/meredian/ciquantum">Source</a><br/>
|
70
|
-
<a href="https://github.com/meredian/ciquantum/issues">Issues</a><br/>
|
71
|
-
<a href="https://github.com/meredian/ciquantum/tree/v<%= CIQuantum::VERSION %>">v<%= CIQuantum::VERSION %></a>
|
72
|
-
</p>
|
73
|
-
</div>
|
74
|
-
<div class="contact">
|
75
|
-
<p>
|
76
|
-
Designed by <a href="http://tom.preston-werner.com/">Tom Preston-Werner</a><br/>
|
77
|
-
Influenced by <a href="http://integrityapp.com/">Integrity</a><br/>
|
78
|
-
Built with <a href="http://sinatrarb.com/">Sinatra</a><br/>
|
79
|
-
Keep it simple, Sam.
|
80
|
-
</p>
|
81
|
-
</div>
|
82
|
-
<div class="rss">
|
83
|
-
<a href="http://github.com/meredian/ciquantum">
|
84
|
-
<img src="<%= ciquantum_root %>/octocat.png" alt="Octocat!" />
|
85
|
-
</a>
|
86
|
-
</div>
|
87
|
-
</div>
|
88
69
|
</div>
|
89
70
|
</body>
|
90
71
|
</html>
|
data/sample.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'sq_auth'
|
3
|
+
|
4
|
+
class Server < Sinatra::Base
|
5
|
+
enable :sessions
|
6
|
+
set :port, 5555
|
7
|
+
|
8
|
+
SqAuth.connect(host: "sqauth.socialquantum.com", port: 443) do |config|
|
9
|
+
config.project_name = "Enchanted"
|
10
|
+
end
|
11
|
+
|
12
|
+
get ["ci::view"], '/?' do
|
13
|
+
"Hello world. Authorize_info: #{access_for("ci::view"){"You can see it"}}"
|
14
|
+
end
|
15
|
+
end
|
data/test/test_hooks.rb
CHANGED
@@ -18,19 +18,21 @@ class File
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# #mock file to be the file I want
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
module CIQuantum
|
22
|
+
class Core
|
23
|
+
attr_writer :last_build
|
24
|
+
alias orig_path_in_project path_in_project
|
25
|
+
alias orig_git_user_and_project git_user_and_project
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
def path_in_project(f)
|
28
|
+
return '/tmp/test' if $hook_override
|
29
|
+
orig_path_in_project
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
def git_user_and_project
|
33
|
+
return ['mine','yours'] if $hook_override
|
34
|
+
orig_git_user_and_project
|
35
|
+
end
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ciquantum
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-05-03 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -109,6 +109,38 @@ dependencies:
|
|
109
109
|
- - ! '>='
|
110
110
|
- !ruby/object:Gem::Version
|
111
111
|
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: pony
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
type: :runtime
|
121
|
+
prerelease: false
|
122
|
+
version_requirements: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
- !ruby/object:Gem::Dependency
|
129
|
+
name: sq_auth
|
130
|
+
requirement: !ruby/object:Gem::Requirement
|
131
|
+
none: false
|
132
|
+
requirements:
|
133
|
+
- - ! '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: 0.0.21
|
136
|
+
type: :runtime
|
137
|
+
prerelease: false
|
138
|
+
version_requirements: !ruby/object:Gem::Requirement
|
139
|
+
none: false
|
140
|
+
requirements:
|
141
|
+
- - ! '>='
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: 0.0.21
|
112
144
|
- !ruby/object:Gem::Dependency
|
113
145
|
name: rack-test
|
114
146
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,22 +190,22 @@ files:
|
|
158
190
|
- ciquantum.gemspec
|
159
191
|
- examples/build-failed
|
160
192
|
- examples/build-worked
|
161
|
-
- examples/
|
162
|
-
- examples/
|
193
|
+
- examples/ciquantum.ru
|
194
|
+
- examples/ciquantumd
|
163
195
|
- lib/ciquantum.rb
|
164
|
-
- lib/ciquantum/adapter.rb
|
165
196
|
- lib/ciquantum/build.rb
|
166
197
|
- lib/ciquantum/commit.rb
|
167
198
|
- lib/ciquantum/config.rb
|
168
|
-
- lib/ciquantum/
|
169
|
-
- lib/ciquantum/public/octocat.png
|
199
|
+
- lib/ciquantum/core.rb
|
170
200
|
- lib/ciquantum/public/screen.css
|
171
201
|
- lib/ciquantum/queue.rb
|
172
202
|
- lib/ciquantum/server.rb
|
173
|
-
- lib/ciquantum/
|
203
|
+
- lib/ciquantum/unfuddle.rb
|
204
|
+
- lib/ciquantum/unfuddle/changeset.rb
|
174
205
|
- lib/ciquantum/version.rb
|
175
206
|
- lib/ciquantum/views/json.erb
|
176
207
|
- lib/ciquantum/views/template.erb
|
208
|
+
- sample.rb
|
177
209
|
- test/fixtures/payload.json
|
178
210
|
- test/helper.rb
|
179
211
|
- test/test_ciquantum_queue.rb
|
data/lib/ciquantum/adapter.rb
DELETED
Binary file
|
Binary file
|