Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/harbormaster/engine/HarbormasterTargetEngine.php
12256 views
1
<?php
2
3
final class HarbormasterTargetEngine extends Phobject {
4
5
private $viewer;
6
private $object;
7
private $autoTargetKeys;
8
9
public function setViewer(PhabricatorUser $viewer) {
10
$this->viewer = $viewer;
11
return $this;
12
}
13
14
public function getViewer() {
15
return $this->viewer;
16
}
17
18
public function setObject(HarbormasterBuildableInterface $object) {
19
$this->object = $object;
20
return $this;
21
}
22
23
public function getObject() {
24
return $this->object;
25
}
26
27
public function setAutoTargetKeys(array $auto_keys) {
28
$this->autoTargetKeys = $auto_keys;
29
return $this;
30
}
31
32
public function getAutoTargetKeys() {
33
return $this->autoTargetKeys;
34
}
35
36
public function buildTargets() {
37
$object = $this->getObject();
38
$viewer = $this->getViewer();
39
40
$step_map = $this->generateBuildStepMap($this->getAutoTargetKeys());
41
42
$buildable = HarbormasterBuildable::createOrLoadExisting(
43
$viewer,
44
$object->getHarbormasterBuildablePHID(),
45
$object->getHarbormasterContainerPHID());
46
47
$target_map = $this->generateBuildTargetMap($buildable, $step_map);
48
49
return $target_map;
50
}
51
52
53
/**
54
* Get a map of the @{class:HarbormasterBuildStep} objects for a list of
55
* autotarget keys.
56
*
57
* This method creates the steps if they do not yet exist.
58
*
59
* @param list<string> Autotarget keys, like `"core.arc.lint"`.
60
* @return map<string, object> Map of keys to step objects.
61
*/
62
private function generateBuildStepMap(array $autotargets) {
63
$viewer = $this->getViewer();
64
65
$autosteps = $this->getAutosteps($autotargets);
66
$autosteps = mgroup($autosteps, 'getBuildStepAutotargetPlanKey');
67
68
$plans = id(new HarbormasterBuildPlanQuery())
69
->setViewer($viewer)
70
->withPlanAutoKeys(array_keys($autosteps))
71
->needBuildSteps(true)
72
->execute();
73
$plans = mpull($plans, null, 'getPlanAutoKey');
74
75
// NOTE: When creating the plan and steps, we save the autokeys as the
76
// names. These won't actually be shown in the UI, but make the data more
77
// consistent for secondary consumers like typeaheads.
78
79
$step_map = array();
80
foreach ($autosteps as $plan_key => $steps) {
81
$plan = idx($plans, $plan_key);
82
if (!$plan) {
83
$plan = HarbormasterBuildPlan::initializeNewBuildPlan($viewer)
84
->setName($plan_key)
85
->setPlanAutoKey($plan_key);
86
}
87
88
$current = $plan->getBuildSteps();
89
$current = mpull($current, null, 'getStepAutoKey');
90
$new_steps = array();
91
92
foreach ($steps as $step_key => $step) {
93
if (isset($current[$step_key])) {
94
$step_map[$step_key] = $current[$step_key];
95
continue;
96
}
97
98
$new_step = HarbormasterBuildStep::initializeNewStep($viewer)
99
->setName($step_key)
100
->setClassName(get_class($step))
101
->setStepAutoKey($step_key);
102
103
$new_steps[$step_key] = $new_step;
104
}
105
106
if ($new_steps) {
107
$plan->openTransaction();
108
if (!$plan->getPHID()) {
109
$plan->save();
110
}
111
foreach ($new_steps as $step_key => $step) {
112
$step->setBuildPlanPHID($plan->getPHID());
113
$step->save();
114
115
$step->attachBuildPlan($plan);
116
$step_map[$step_key] = $step;
117
}
118
$plan->saveTransaction();
119
}
120
}
121
122
return $step_map;
123
}
124
125
126
/**
127
* Get all of the @{class:HarbormasterBuildStepImplementation} objects for
128
* a list of autotarget keys.
129
*
130
* @param list<string> Autotarget keys, like `"core.arc.lint"`.
131
* @return map<string, object> Map of keys to implementations.
132
*/
133
private function getAutosteps(array $autotargets) {
134
$all_steps = HarbormasterBuildStepImplementation::getImplementations();
135
$all_steps = mpull($all_steps, null, 'getBuildStepAutotargetStepKey');
136
137
// Make sure all the targets really exist.
138
foreach ($autotargets as $autotarget) {
139
if (empty($all_steps[$autotarget])) {
140
throw new Exception(
141
pht(
142
'No build step provides autotarget "%s"!',
143
$autotarget));
144
}
145
}
146
147
return array_select_keys($all_steps, $autotargets);
148
}
149
150
151
/**
152
* Get a list of @{class:HarbormasterBuildTarget} objects for a list of
153
* autotarget keys.
154
*
155
* If some targets or builds do not exist, they are created.
156
*
157
* @param HarbormasterBuildable A buildable.
158
* @param map<string, object> Map of keys to steps.
159
* @return map<string, object> Map of keys to targets.
160
*/
161
private function generateBuildTargetMap(
162
HarbormasterBuildable $buildable,
163
array $step_map) {
164
165
$viewer = $this->getViewer();
166
$initiator_phid = null;
167
if (!$viewer->isOmnipotent()) {
168
$initiator_phid = $viewer->getPHID();
169
}
170
$plan_map = mgroup($step_map, 'getBuildPlanPHID');
171
172
$builds = id(new HarbormasterBuildQuery())
173
->setViewer($viewer)
174
->withBuildablePHIDs(array($buildable->getPHID()))
175
->withBuildPlanPHIDs(array_keys($plan_map))
176
->needBuildTargets(true)
177
->execute();
178
179
$autobuilds = array();
180
foreach ($builds as $build) {
181
$plan_key = $build->getBuildPlan()->getPlanAutoKey();
182
$autobuilds[$plan_key] = $build;
183
}
184
185
$new_builds = array();
186
foreach ($plan_map as $plan_phid => $steps) {
187
$plan = head($steps)->getBuildPlan();
188
$plan_key = $plan->getPlanAutoKey();
189
190
$build = idx($autobuilds, $plan_key);
191
if ($build) {
192
// We already have a build for this set of targets, so we don't need
193
// to do any work. (It's possible the build is an older build that
194
// doesn't have all of the right targets if new autotargets were
195
// recently introduced, but we don't currently try to construct them.)
196
continue;
197
}
198
199
// NOTE: Normally, `applyPlan()` does not actually generate targets.
200
// We need to apply the plan in-process to perform target generation.
201
// This is fine as long as autotargets are empty containers that don't
202
// do any work, which they always should be.
203
204
PhabricatorWorker::setRunAllTasksInProcess(true);
205
try {
206
207
// NOTE: We might race another process here to create the same build
208
// with the same `planAutoKey`. The database will prevent this and
209
// using autotargets only currently makes sense if you just created the
210
// resource and "own" it, so we don't try to handle this, but may need
211
// to be more careful here if use of autotargets expands.
212
213
$build = $buildable->applyPlan($plan, array(), $initiator_phid);
214
PhabricatorWorker::setRunAllTasksInProcess(false);
215
} catch (Exception $ex) {
216
PhabricatorWorker::setRunAllTasksInProcess(false);
217
throw $ex;
218
}
219
220
$new_builds[] = $build;
221
}
222
223
if ($new_builds) {
224
$all_targets = id(new HarbormasterBuildTargetQuery())
225
->setViewer($viewer)
226
->withBuildPHIDs(mpull($new_builds, 'getPHID'))
227
->execute();
228
} else {
229
$all_targets = array();
230
}
231
232
foreach ($builds as $build) {
233
foreach ($build->getBuildTargets() as $target) {
234
$all_targets[] = $target;
235
}
236
}
237
238
$target_map = array();
239
foreach ($all_targets as $target) {
240
$target_key = $target
241
->getImplementation()
242
->getBuildStepAutotargetStepKey();
243
if (!$target_key) {
244
continue;
245
}
246
$target_map[$target_key] = $target;
247
}
248
249
$target_map = array_select_keys($target_map, array_keys($step_map));
250
251
return $target_map;
252
}
253
254
255
}
256
257