ciquantum 0.0.3 → 0.0.6
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/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
|