@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class HarbormasterLeaseWorkingCopyBuildStepImplementation
4 extends HarbormasterBuildStepImplementation {
5
6 public function getName() {
7 return pht('Lease Working Copy');
8 }
9
10 public function getGenericDescription() {
11 return pht('Build a working copy in Drydock.');
12 }
13
14 public function getBuildStepGroupKey() {
15 return HarbormasterDrydockBuildStepGroup::GROUPKEY;
16 }
17
18 public function execute(
19 HarbormasterBuild $build,
20 HarbormasterBuildTarget $build_target) {
21 $viewer = PhabricatorUser::getOmnipotentUser();
22
23 $settings = $this->getSettings();
24
25 // TODO: We should probably have a separate temporary storage area for
26 // execution stuff that doesn't step on configuration state?
27 $lease_phid = $build_target->getDetail('exec.leasePHID');
28
29 if ($lease_phid) {
30 $lease = id(new DrydockLeaseQuery())
31 ->setViewer($viewer)
32 ->withPHIDs(array($lease_phid))
33 ->executeOne();
34 if (!$lease) {
35 throw new PhabricatorWorkerPermanentFailureException(
36 pht(
37 'Lease "%s" could not be loaded.',
38 $lease_phid));
39 }
40 } else {
41 $working_copy_type = id(new DrydockWorkingCopyBlueprintImplementation())
42 ->getType();
43
44 $allowed_phids = $build_target->getFieldValue('blueprintPHIDs');
45 if (!is_array($allowed_phids)) {
46 $allowed_phids = array();
47 }
48 $authorizing_phid = $build_target->getBuildStep()->getPHID();
49
50 $lease = DrydockLease::initializeNewLease()
51 ->setResourceType($working_copy_type)
52 ->setOwnerPHID($build_target->getPHID())
53 ->setAuthorizingPHID($authorizing_phid)
54 ->setAllowedBlueprintPHIDs($allowed_phids);
55
56 $map = $this->buildRepositoryMap($build_target);
57
58 $lease->setAttribute('repositories.map', $map);
59
60 $task_id = $this->getCurrentWorkerTaskID();
61 if ($task_id) {
62 $lease->setAwakenTaskIDs(array($task_id));
63 }
64
65 // TODO: Maybe add a method to mark artifacts like this as pending?
66
67 // Create the artifact now so that the lease is always disposed of, even
68 // if this target is aborted.
69 $build_target->createArtifact(
70 $viewer,
71 $settings['name'],
72 HarbormasterWorkingCopyArtifact::ARTIFACTCONST,
73 array(
74 'drydockLeasePHID' => $lease->getPHID(),
75 ));
76
77 $lease->queueForActivation();
78
79 $build_target
80 ->setDetail('exec.leasePHID', $lease->getPHID())
81 ->save();
82 }
83
84 if ($lease->isActivating()) {
85 // TODO: Smart backoff?
86 throw new PhabricatorWorkerYieldException(15);
87 }
88
89 if (!$lease->isActive()) {
90 // TODO: We could just forget about this lease and retry?
91 throw new PhabricatorWorkerPermanentFailureException(
92 pht(
93 'Lease "%s" never activated.',
94 $lease->getPHID()));
95 }
96 }
97
98 public function getArtifactOutputs() {
99 return array(
100 array(
101 'name' => pht('Working Copy'),
102 'key' => $this->getSetting('name'),
103 'type' => HarbormasterWorkingCopyArtifact::ARTIFACTCONST,
104 ),
105 );
106 }
107
108 public function getFieldSpecifications() {
109 return array(
110 'name' => array(
111 'name' => pht('Artifact Name'),
112 'type' => 'text',
113 'required' => true,
114 ),
115 'blueprintPHIDs' => array(
116 'name' => pht('Use Blueprints'),
117 'type' => 'blueprints',
118 'required' => true,
119 ),
120 'repositoryPHIDs' => array(
121 'name' => pht('Also Clone'),
122 'type' => 'datasource',
123 'datasource.class' => 'DiffusionRepositoryDatasource',
124 ),
125 );
126 }
127
128 private function buildRepositoryMap(HarbormasterBuildTarget $build_target) {
129 $viewer = PhabricatorUser::getOmnipotentUser();
130 $variables = $build_target->getVariables();
131
132 $repository_phid = idx($variables, 'repository.phid');
133 if (!$repository_phid) {
134 throw new Exception(
135 pht(
136 'Unable to determine how to clone the repository for this '.
137 'buildable: it is not associated with a tracked repository.'));
138 }
139
140 $also_phids = $build_target->getFieldValue('repositoryPHIDs');
141 if (!is_array($also_phids)) {
142 $also_phids = array();
143 }
144
145 $all_phids = $also_phids;
146 $all_phids[] = $repository_phid;
147
148 $repositories = id(new PhabricatorRepositoryQuery())
149 ->setViewer($viewer)
150 ->withPHIDs($all_phids)
151 ->execute();
152 $repositories = mpull($repositories, null, 'getPHID');
153
154 foreach ($all_phids as $phid) {
155 if (empty($repositories[$phid])) {
156 throw new PhabricatorWorkerPermanentFailureException(
157 pht(
158 'Unable to load repository with PHID "%s".',
159 $phid));
160 }
161 }
162
163 $map = array();
164
165 foreach ($also_phids as $also_phid) {
166 $also_repo = $repositories[$also_phid];
167 $map[$also_repo->getCloneName()] = array(
168 'phid' => $also_repo->getPHID(),
169 'branch' => 'master',
170 );
171 }
172
173 $repository = $repositories[$repository_phid];
174
175 $commit = idx($variables, 'buildable.commit');
176 $ref_uri = idx($variables, 'repository.staging.uri');
177 $ref_ref = idx($variables, 'repository.staging.ref');
178 if ($commit) {
179 $spec = array(
180 'commit' => $commit,
181 );
182 } else if ($ref_uri && $ref_ref) {
183 $spec = array(
184 'ref' => array(
185 'uri' => $ref_uri,
186 'ref' => $ref_ref,
187 ),
188 );
189 } else {
190 throw new Exception(
191 pht(
192 'Unable to determine how to fetch changes: this buildable does not '.
193 'identify a commit or a staging ref. You may need to configure a '.
194 'repository staging area.'));
195 }
196
197 $directory = $repository->getCloneName();
198 $map[$directory] = array(
199 'phid' => $repository->getPHID(),
200 'default' => true,
201 ) + $spec;
202
203 return $map;
204 }
205
206}