forked from travis-ci/travis-core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.rb
220 lines (180 loc) · 6.53 KB
/
build.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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
140
141
142
143
144
145
146
147
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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
require 'active_record'
require 'core_ext/active_record/base'
require 'core_ext/hash/deep_symbolize_keys'
require 'simple_states'
# Build currently models a central but rather abstract domain entity: the thing
# that is triggered by a Github request (service hook ping).
#
# Build groups a matrix of Job::Test instances, and belongs to a Request (and
# thus Commit as well as a Repository).
#
# A Build is created when its Request was configured (by fetching .travis.yml)
# and approved (e.g. not excluded by the configuration). Once a Build is
# created it will expand its matrix according to the given configuration and
# create the according Job::Test instances. Each Job::Test instance will
# trigger a test run remotely (on the worker). Once all Job::Test instances
# have finished the Build will be finished as well.
#
# Each of these state changes (build:created, job:started, job:finished, ...)
# will issue events that are listened for by the event handlers contained in
# travis/notification. These event handlers then send out various notifications
# of various types through email, pusher and irc, archive builds and queue
# jobs for the workers.
#
# Build is split up to several modules:
#
# * Build - ActiveRecord structure, validations and scopes
# * States - state definitions and events
# * Denormalize - some state changes denormalize attributes to the build's
# repository (e.g. Build#started_at gets propagated to
# Repository#last_started_at)
# * Matrix - logic related to expanding the build matrix, normalizing
# configuration for Job::Test instances, evaluating the
# final build result etc.
# * Messages - helpers for evaluating human readable result messages
# (e.g. "Still Failing")
# * Events - helpers that are used by notification handlers (and that
# TODO probably should be cleaned up and moved to
# travis/notification)
class Build < Travis::Model
require 'travis/model/build/config'
require 'travis/model/build/denormalize'
require 'travis/model/build/matrix'
require 'travis/model/build/metrics'
require 'travis/model/build/result_message'
require 'travis/model/build/states'
require 'travis/model/env_helpers'
include Matrix, States, SimpleStates
belongs_to :commit
belongs_to :request
belongs_to :repository, autosave: true
belongs_to :owner, polymorphic: true
has_many :matrix, as: :source, order: :id, class_name: 'Job::Test', dependent: :destroy
has_many :events, as: :source
validates :repository_id, :commit_id, :request_id, presence: true
serialize :config
delegate :same_repo_pull_request?, :to => :request
class << self
def recent(options = {})
where('state IN (?)', state_names - [:created, :queued]).order(arel_table[:started_at].desc).paged(options)
end
def was_started
where('state <> ?', :created)
end
def finished
where(state: [:finished, :passed, :failed, :errored, :canceled]) # TODO extract
end
def on_state(state)
where(state.present? ? ['builds.state IN (?)', state] : [])
end
def on_branch(branch)
pushes.where(branch.present? ? ['branch IN (?)', normalize_to_array(branch)] : [])
end
def by_event_type(event_type)
event_type == 'pull_request' ? pull_requests : pushes
end
def pushes
where(:event_type => 'push')
end
def pull_requests
where(event_type: 'pull_request')
end
def previous(build)
where('builds.repository_id = ? AND builds.id < ?', build.repository_id, build.id).finished.descending.limit(1).first
end
def descending
order(arel_table[:id].desc)
end
def paged(options)
page = (options[:page] || 1).to_i
limit(per_page).offset(per_page * (page - 1))
end
def last_build_on(options)
scope = descending
scope = scope.on_state(options[:state]) if options[:state]
scope = scope.on_branch(options[:branch]) if options[:branch]
scope.first
end
def last_state_on(options)
last_build_on(options).try(:state).try(:to_sym)
end
def older_than(build = nil)
scope = descending.paged({}) # TODO in which case we'd call older_than without an argument?
scope = scope.where('number::integer < ?', (build.is_a?(Build) ? build.number : build).to_i) if build
scope
end
def next_number
maximum('number::int4').to_i + 1
end
protected
def normalize_to_array(object)
Array(object).compact.join(',').split(',')
end
def per_page
25
end
end
after_initialize do
self.config = {} if config.nil?
end
# set the build number and expand the matrix
before_create do
self.number = repository.builds.next_number
self.previous_state = last_finished_state_on_branch
self.event_type = request.event_type
self.pull_request_title = request.pull_request_title
self.pull_request_number = request.pull_request_number
self.branch = commit.branch
expand_matrix
end
after_save do
unless cached_matrix_ids
update_column(:cached_matrix_ids, to_postgres_array(matrix_ids))
end
end
# AR 3.2 does not handle pg arrays and the plugins supporting them
# do not work well with jdbc drivers
# TODO: remove this once we're on >= 4.0
def cached_matrix_ids
if (value = super) && value =~ /^{/
value.gsub(/^{|}$/, '').split(',').map(&:to_i)
end
end
def matrix_ids
matrix.map(&:id)
end
def secure_env_enabled?
!pull_request? || same_repo_pull_request?
end
alias addons_enabled? secure_env_enabled?
def config=(config)
super(Config.new(config, multi_os: multi_os_enabled?).normalize)
end
def obfuscated_config
Config.new(config, key_fetcher: lambda { self.repository.key }).obfuscate
end
def cancelable?
matrix.any? { |job| job.cancelable? }
end
def pull_request?
event_type == 'pull_request'
end
# COMPAT: used in http api v1, deprecate as soon as v1 gets retired
def result
state.try(:to_sym) == :passed ? 0 : 1
end
def on_default_branch?
branch == repository.default_branch
end
private
def multi_os_enabled?
Travis::Features.enabled_for_all?(:multi_os) || repository && Travis::Features.active?(:multi_os, repository)
end
def last_finished_state_on_branch
repository.builds.finished.last_state_on(branch: commit.branch)
end
def to_postgres_array(ids)
ids = ids.compact.uniq
"{#{ids.map { |id| id.to_i.to_s }.join(',')}}" unless ids.empty?
end
end