Class: CIQuantum::Core

Inherits:
Object
  • Object
show all
Defined in:
lib/ciquantum/core.rb

Constant Summary collapse

HistoryLimit =
20

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_path) ⇒ Core

Returns a new instance of Core.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/ciquantum/core.rb', line 17

def initialize(project_path)
  @is_building = false
  @project_path = File.expand_path(project_path)
  @adapter = Adapter.from_config(repo_config)

  @projectname = repo_config.projectname.to_s
  @coverage_path = repo_config.coveragepath.to_s
  @git = Git.new(@project_path)

  @last_build = nil
  @current_build = nil
  @queue = Queue.new(!repo_config.buildqueue.to_s.empty?, true)

  trap("INT") { stop }
end

Instance Attribute Details

#adapterObject (readonly)

Returns the value of attribute adapter.



5
6
7
# File 'lib/ciquantum/core.rb', line 5

def adapter
  @adapter
end

#coverage_pathObject (readonly)

Returns the value of attribute coverage_path.



10
11
12
# File 'lib/ciquantum/core.rb', line 10

def coverage_path
  @coverage_path
end

#current_buildObject (readonly)

Returns the value of attribute current_build.



7
8
9
# File 'lib/ciquantum/core.rb', line 7

def current_build
  @current_build
end

#gitObject (readonly)

Returns the value of attribute git.



11
12
13
# File 'lib/ciquantum/core.rb', line 11

def git
  @git
end

#is_buildingObject (readonly)

Returns the value of attribute is_building.



12
13
14
# File 'lib/ciquantum/core.rb', line 12

def is_building
  @is_building
end

#last_buildObject (readonly)

Returns the value of attribute last_build.



8
9
10
# File 'lib/ciquantum/core.rb', line 8

def last_build
  @last_build
end

#projectnameObject (readonly)

Returns the value of attribute projectname.



9
10
11
# File 'lib/ciquantum/core.rb', line 9

def projectname
  @projectname
end

#shared_pathObject

Returns the value of attribute shared_path.



13
14
15
# File 'lib/ciquantum/core.rb', line 13

def shared_path
  @shared_path
end

#urlObject (readonly)

Returns the value of attribute url.



6
7
8
# File 'lib/ciquantum/core.rb', line 6

def url
  @url
end

Instance Method Details

#active_buildsObject



245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/ciquantum/core.rb', line 245

def active_builds
  active_builds = []
  old_builds.each do |build_file|
    build = read_build(build_file)
    active_builds << build
    if build.status == :worked || active_builds.size >= HistoryLimit
      remove_elder_builds build
      break
    end
  end
  active_builds
end

#build(branch = nil) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/ciquantum/core.rb', line 79

def build(branch=nil)
  branch ||= tracked_branch
  puts "Preparing branch build with param:#{branch}"
  if building?
    @queue.append_unless_already_exists(branch)
    return
  end

  @is_building = true
  @current_build = Build.new(@project_path)
  write_current_build

  Thread.new {
    build!(branch)
  }

  @current_build = read_current_build
end

#build!(branch) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/ciquantum/core.rb', line 112

def build!(branch)
  build = @current_build
  update_tracked_branch branch
  @current_build = read_current_build
  output = ''
  build.sha = @git.sha
  build.branch = @git.branch
  write_build 'current', build
  run_hook "prebuild"

  open_pipe("cd #{@project_path} && #{runner_command} 2>&1") do |pipe, pid|
    puts "#{Time.now.to_i}: Building #{build.branch} at #{build.short_sha}: pid=#{pid}"

    build.pid = pid
    write_build 'current', build
    output = pipe.read
  end

  Process.waitpid(build.pid, 1)
  status = $?.exitstatus.to_i
  @current_build = build
  puts "#{Time.now.to_i}: Built #{build.short_sha}: status=#{status}"

  status == 0 ? build_worked(output) : build_failed('', output)
rescue Object => e
  puts "Exception building: #{e.message} (#{e.class})"
  build_failed('', e.to_s)
end

#build_failed(output, error) ⇒ Object



53
54
55
56
# File 'lib/ciquantum/core.rb', line 53

def build_failed(output, error)
  finish_build :failed, "#{error}\n\n#{output}"
  run_hook "build-failed"
end

#build_worked(output) ⇒ Object



58
59
60
61
# File 'lib/ciquantum/core.rb', line 58

def build_worked(output)
  finish_build :worked, output
  run_hook "build-worked"
end

#building?Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/ciquantum/core.rb', line 33

def building?
  !!@current_build
end

#coverage_for_build(build) ⇒ Object



195
196
197
# File 'lib/ciquantum/core.rb', line 195

def coverage_for_build build
  File.join build.id, "coverage"
end

#coverage_path_for_build(build) ⇒ Object



199
200
201
# File 'lib/ciquantum/core.rb', line 199

def coverage_path_for_build build
  File.join @shared_path, coverage_for_build(build)
end

#finish_build(status, output) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/ciquantum/core.rb', line 63

def finish_build(status, output)
  @current_build.finished_at = Time.now
  @current_build.status = status
  @current_build.output = output
  @last_build = @current_build

  @current_build = nil
  write_current_build
  write_last_build
  run_hook "postbuild"
  save_coverage_for_build @last_build
  @is_building = false

  build(@queue.next_branch_to_build) if @queue.waiting?
end

#old_buildsObject



258
259
260
261
262
263
264
# File 'lib/ciquantum/core.rb', line 258

def old_builds
  Dir.glob(path_in_project(".git/builds/*")).collect do |f|
    File.basename(f)
  end.select do |f|
    f =~ /\d+/
  end.sort.reverse
end

#open_pipe(cmd) {|read, pid| ... } ⇒ Object

Yields:



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/ciquantum/core.rb', line 98

def open_pipe(cmd)
  read, write = IO.pipe

  pid = fork do
    read.close
    $stdout.reopen write
    exec cmd
  end

  write.close

  yield read, pid
end

#path_in_project(path) ⇒ Object



191
192
193
# File 'lib/ciquantum/core.rb', line 191

def path_in_project path
  File.join(@project_path, path)
end

#pidObject



37
38
39
# File 'lib/ciquantum/core.rb', line 37

def pid
  building? and current_build.pid
end

#read_build(name) ⇒ Object



227
228
229
230
231
# File 'lib/ciquantum/core.rb', line 227

def read_build(name)
  build = Build.load(path_in_project(".git/builds/#{name}"), @project_path)
  build.commit.get_url! adapter if build && build.commit && adapter
  build
end

#read_build_by_index(index) ⇒ Object



241
242
243
# File 'lib/ciquantum/core.rb', line 241

def read_build_by_index index
  old_builds[index] ? read_build(old_builds[index]) : nil
end

#read_current_buildObject



237
238
239
# File 'lib/ciquantum/core.rb', line 237

def read_current_build
  read_build 'current'
end

#read_last_buildObject



233
234
235
# File 'lib/ciquantum/core.rb', line 233

def read_last_build
  read_build_by_index 0
end

#remove_elder_builds(limit_build) ⇒ Object



284
285
286
287
288
289
290
291
292
# File 'lib/ciquantum/core.rb', line 284

def remove_elder_builds limit_build
  old_builds.each do |build_file|
    build = read_build(build_file)
    if build.finished_at < limit_build.finished_at
      FileUtils.rm_r path_in_project(".git/builds/#{build_file}")
      FileUtils.rm_r coverage_path_for_build build
    end
  end
end

#repo_configObject



211
212
213
# File 'lib/ciquantum/core.rb', line 211

def repo_config
  Config.ciquantum(@project_path)
end

#restoreObject



177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/ciquantum/core.rb', line 177

def restore
  @last_build = read_last_build
  @current_build = read_current_build

  if @current_build && @current_build.pid
    Process.kill(0, @current_build.pid)
  else
    @current_build = nil
  end

rescue Errno::ESRCH
  @current_build = nil
end

#run_hook(hook) ⇒ Object

massage our repo



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/ciquantum/core.rb', line 148

def run_hook(hook)
  file = path_in_project(".git/hooks/#{hook}")
  if File.exists?(file) && File.executable?(file)
    data =
      if @last_build && @last_build.commit
        @last_build.commit.get_url! @adapter
        {
          "MESSAGE" => @last_build.commit.message,
          "AUTHOR" => @last_build.commit.author,
          "SHA" => @last_build.commit.sha,
          "URL" => @last_build.commit.url,
          "OUTPUT" => @last_build.env_output
        }
      else
        {}
      end

    orig_ENV = ENV.to_hash
    # ENV.clear
    data.each{ |k, v| ENV[k] = v }
    puts "Executing hook: #{file}"
    output = `cd #{@project_path} && #{file}`

    ENV.clear
    orig_ENV.to_hash.each{ |k, v| ENV[k] = v}
    output
  end
end

#runner_commandObject

shellin’ out



142
143
144
145
# File 'lib/ciquantum/core.rb', line 142

def runner_command
  runner = repo_config.runner.to_s
  runner == '' ? "rake -s --color test:units" : runner
end

#save_coverage_for_build(build) ⇒ Object



203
204
205
206
207
208
209
# File 'lib/ciquantum/core.rb', line 203

def save_coverage_for_build build
  return unless @coverage_path
  build_coverage_path = coverage_path_for_build(build)
  puts "Coverage path: #{build_coverage_path}"
  FileUtils.mkdir_p build_coverage_path
  FileUtils.cp_r "#{path_in_project(@coverage_path)}/.", build_coverage_path
end

#stopObject



41
42
43
44
# File 'lib/ciquantum/core.rb', line 41

def stop
  stop_build
  exit!
end

#stop_buildObject



46
47
48
49
50
51
# File 'lib/ciquantum/core.rb', line 46

def stop_build
  @is_building = false
  Process.kill(9, @current_build.pid) if @current_build && @current_build.pid
  write_build 'current', nil
  @current_build = nil
end

#tracked_branchObject



222
223
224
225
# File 'lib/ciquantum/core.rb', line 222

def tracked_branch
  branch = repo_config.branch.to_s
  @tracked_branch ||= (branch.empty? ? "master" : branch.strip)
end

#update_tracked_branch(branch) ⇒ Object



215
216
217
218
219
220
# File 'lib/ciquantum/core.rb', line 215

def update_tracked_branch branch
  new_branch = branch.strip
  repo_config.branch.set new_branch
  @tracked_branch = new_branch
  @git.checkout new_branch
end

#write_build(name, build) ⇒ Object



274
275
276
277
278
279
280
281
282
# File 'lib/ciquantum/core.rb', line 274

def write_build(name, build)
  filename = path_in_project(".git/builds/#{name}")
  FileUtils.mkdir_p path_in_project('.git/builds')
  if build
    build.dump filename
  elsif File.exist?(filename)
    File.unlink filename
  end
end

#write_current_buildObject



270
271
272
# File 'lib/ciquantum/core.rb', line 270

def write_current_build
  write_build 'current', @current_build
end

#write_last_buildObject



266
267
268
# File 'lib/ciquantum/core.rb', line 266

def write_last_build
  write_build @last_build.finished_at.to_i.to_s, @last_build
end