1 | <?php |
||
2 | /** |
||
3 | * @file |
||
4 | * Actions for Deployotron. |
||
5 | */ |
||
6 | |||
7 | namespace Deployotron { |
||
8 | |||
9 | define('COLOR_RED', "\033[1;31;40m\033[1m"); |
||
10 | define('COLOR_YELLOW', "\033[1;33;40m\033[1m"); |
||
11 | define('COLOR_GREEN', "\033[1;32;40m\033[1m"); |
||
12 | define('COLOR_RESET', "\033[0m"); |
||
13 | |||
14 | /** |
||
15 | * Creates actions to run. |
||
16 | */ |
||
17 | class ActionFactory { |
||
18 | static protected $actionMapping = array( |
||
19 | 'deploy' => array( |
||
20 | 'SanityCheck', |
||
21 | 'SiteOffline', |
||
22 | 'BackupDatabase', |
||
23 | 'DeployCode', |
||
24 | 'CreateVersionTxt', |
||
25 | 'UpdateDatabase', |
||
26 | 'ClearCache', |
||
27 | 'SiteOnline', |
||
28 | 'PurgeDatabaseBackups', |
||
29 | 'FlowdockNotificaton', |
||
30 | 'NewRelicNotificaton', |
||
31 | ), |
||
32 | 'omg' => array( |
||
33 | 'SiteOffline', |
||
34 | 'OMGPrepare', |
||
35 | 'DeployCode', |
||
36 | 'CreateVersionTxt', |
||
37 | 'RestoreDatabase', |
||
38 | 'SiteOnline', |
||
39 | 'ClearCache', |
||
40 | ), |
||
41 | ); |
||
42 | |||
43 | /** |
||
44 | * Gathers an array of actions to run. |
||
45 | */ |
||
46 | static public function getActions($name, $site = NULL) { |
||
47 | $actions = array(); |
||
48 | if (isset(static::$actionMapping[$name])) { |
||
49 | foreach (static::$actionMapping[$name] as $class_name) { |
||
50 | $class_name = '\\Deployotron\\Actions\\' . $class_name; |
||
51 | $action = new $class_name($site); |
||
52 | $actions = array_merge($actions, |
||
53 | static::getActionCommands('pre', $action, $site), |
||
54 | array($action), |
||
55 | static::getActionCommands('post', $action, $site)); |
||
56 | } |
||
57 | return $actions; |
||
58 | } |
||
59 | } |
||
60 | |||
61 | /** |
||
62 | * Gets pre/post commands for an action. |
||
63 | */ |
||
64 | static protected function getActionCommands($type, $action, $site) { |
||
65 | $actions = array(); |
||
66 | $switch = $type . '-' . $action->getSwitchSuffix(); |
||
67 | if ($action->enabled() && !drush_get_option('no-' . $switch, FALSE)) { |
||
68 | // @todo drush_get_option_list() doesn't play well with commas. |
||
69 | if ($commands = drush_get_option_list($switch, FALSE)) { |
||
70 | foreach ($commands as $command) { |
||
71 | if (!empty($command)) { |
||
72 | $actions[] = new Actions\CommandAction($site, $command); |
||
73 | } |
||
74 | } |
||
75 | } |
||
76 | } |
||
77 | |||
78 | return $actions; |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Output help on actions and options. |
||
83 | */ |
||
84 | static public function getHelp() { |
||
85 | $all_actions = array(); |
||
86 | drush_print('Commands'); |
||
87 | drush_print('--------'); |
||
88 | foreach (static::$actionMapping as $name => $actions) { |
||
89 | drush_print(wordwrap(dt('@name runs the actions: @actions', array( |
||
90 | '@name' => $name, |
||
91 | '@actions' => implode(', ', $actions), |
||
92 | )))); |
||
93 | drush_print(); |
||
94 | |||
95 | $all_actions = array_merge($all_actions, $actions); |
||
96 | } |
||
97 | $all_actions = array_unique($all_actions); |
||
98 | sort($all_actions); |
||
99 | |||
100 | drush_print('Actions'); |
||
101 | drush_print('-------'); |
||
102 | foreach ($all_actions as $class_name) { |
||
103 | drush_print($class_name . ':'); |
||
104 | $class_name = '\\Deployotron\\Actions\\' . $class_name; |
||
105 | $action = new $class_name('@self'); |
||
106 | drush_print($action->getHelp()); |
||
107 | drush_print(); |
||
108 | |||
109 | // Print options. |
||
110 | $options = $action->getOptions(); |
||
111 | if ($options) { |
||
112 | drush_print_table(drush_format_help_section($options, 'options')); |
||
113 | drush_print(); |
||
114 | } |
||
115 | drush_print(); |
||
116 | } |
||
117 | |||
118 | } |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * Base class for actions. |
||
123 | */ |
||
124 | abstract class Action { |
||
125 | /** |
||
126 | * Set a default short description. |
||
127 | */ |
||
128 | protected $short = "incompletely implemented"; |
||
129 | |||
130 | /** |
||
131 | * Default run message. |
||
132 | */ |
||
133 | protected $runMessage = "Running action."; |
||
134 | |||
135 | /** |
||
136 | * Options for this action. |
||
137 | */ |
||
138 | protected $options = array(); |
||
139 | |||
140 | /** |
||
141 | * The site this action works on. |
||
142 | */ |
||
143 | protected $site; |
||
144 | |||
145 | /** |
||
146 | * Result object of last drush command. |
||
147 | */ |
||
148 | protected $drushResult; |
||
149 | |||
150 | /** |
||
151 | * Array of the output of the last executed command. |
||
152 | */ |
||
153 | protected $shOutput; |
||
154 | |||
155 | /** |
||
156 | * Return code of the last executed command. |
||
157 | */ |
||
158 | protected $shRc; |
||
159 | |||
160 | /** |
||
161 | * Shas of checkouts of aliases. |
||
162 | */ |
||
163 | static protected $headShas = array(); |
||
164 | |||
165 | /** |
||
166 | * Construct a new Action object. |
||
167 | */ |
||
168 | public function __construct($site) { |
||
169 | $this->site = $site; |
||
170 | |||
171 | if ($this->short == "incompletely implemented") { |
||
172 | drush_log(dt('Incomplete action, missing short description: @action', array('@action' => get_class($this))), 'warning'); |
||
173 | } |
||
174 | |||
175 | if (!isset($this->runMessage)) { |
||
176 | $this->runMessage = 'Running ' . $this->short; |
||
177 | } |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Get help description. |
||
182 | */ |
||
183 | public function getHelp() { |
||
184 | if (isset($this->help)) { |
||
185 | return $this->help; |
||
186 | } |
||
187 | if (isset($this->description)) { |
||
188 | return $this->description; |
||
189 | } |
||
190 | return "No description."; |
||
191 | } |
||
192 | |||
193 | /** |
||
194 | * Get the command line options for this action. |
||
195 | * |
||
196 | * If the command has an enable-switch, the kill-switch and command options |
||
197 | * are classed as sub-options for nice help output. If there's no |
||
198 | * enable-switch, all options are considered regular options. |
||
199 | * |
||
200 | * @return array |
||
201 | * Options and sub-options as defined on commands in hook_drush_command(). |
||
202 | */ |
||
203 | public function getOptions() { |
||
204 | $options = array(); |
||
205 | if (isset($this->killSwitch)) { |
||
206 | $options += array( |
||
207 | $this->killSwitch => "Don't " . $this->short . (isset($this->enableSwitch) ? ' (overrides --' . $this->enableSwitch . ')' : '') . '.', |
||
208 | ); |
||
209 | } |
||
210 | |||
211 | $options += $this->options; |
||
212 | |||
213 | $prest_options = array( |
||
214 | 'pre/post-*' => array( |
||
215 | 'pre-' . $this->getSwitchSuffix() => "Before " . $this->short . ".", |
||
216 | 'post-' . $this->getSwitchSuffix() => "After " . $this->short . ".", |
||
217 | ), |
||
218 | ); |
||
219 | |||
220 | if (isset($this->enableSwitch)) { |
||
221 | $sub_options = $options; |
||
222 | $options = array(); |
||
223 | // If the action options defines the enable switch, use it so |
||
224 | // description and possible example-values propergate. |
||
225 | if (isset($sub_options[$this->enableSwitch])) { |
||
226 | $options[$this->enableSwitch] = $sub_options[$this->enableSwitch]; |
||
227 | unset($sub_options[$this->enableSwitch]); |
||
228 | } |
||
229 | else { |
||
230 | $options[$this->enableSwitch] = ucfirst($this->short) . '.'; |
||
231 | } |
||
232 | $options['pre/post-*'] = "Commands to run before/after action."; |
||
233 | return array( |
||
234 | 'options' => $options, |
||
235 | 'sub-options' => (!empty($sub_options) ? array($this->enableSwitch => $sub_options) : array()) + $prest_options, |
||
236 | ); |
||
237 | } |
||
238 | |||
239 | $options['pre/post-*'] = "Commands to run before/after action."; |
||
240 | return array('options' => $options, 'sub-options' => $prest_options); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * Get the switch suffix for --pre-/--post- switches. |
||
245 | */ |
||
246 | public function getSwitchSuffix() { |
||
247 | if (isset($this->switchSuffix)) { |
||
248 | return $this->switchSuffix; |
||
249 | } |
||
250 | elseif (isset($this->enableSwitch)) { |
||
251 | return $this->enableSwitch; |
||
252 | } |
||
253 | elseif (isset($this->killSwitch)) { |
||
254 | return preg_replace('/^no-/', '', $this->killSwitch); |
||
255 | } |
||
256 | else { |
||
257 | return preg_replace(array('/[^a-z0-9 ]/', '/ /'), array('', '-'), strtolower($this->short)); |
||
258 | } |
||
259 | } |
||
260 | |||
261 | /** |
||
262 | * Get the short description of this action. |
||
263 | * |
||
264 | * If the action has a killswitch, it should work with "Don't " prepended. |
||
265 | * |
||
266 | * @return string |
||
267 | * Human readable short description of action taken. |
||
268 | */ |
||
269 | public function getShort() { |
||
270 | return $this->short; |
||
271 | } |
||
272 | |||
273 | /** |
||
274 | * Return the message to print when running this action. |
||
275 | * |
||
276 | * @return string |
||
277 | * The Message (by Grandmaster Flash). |
||
278 | */ |
||
279 | public function getRunMessage() { |
||
280 | return $this->runMessage; |
||
281 | } |
||
282 | |||
283 | /** |
||
284 | * Get a description of what the action would do. |
||
285 | * |
||
286 | * If the action subclass defines the $description property, use that, else |
||
287 | * returns a generic message. |
||
288 | * |
||
289 | * If more logic than a static string is needed, subclassesare encouraged to |
||
290 | * override this to provide a more specific description of the effect of the |
||
291 | * action. |
||
292 | * |
||
293 | * Used when the user provides the --confirm switch. |
||
294 | * |
||
295 | * @return string |
||
296 | * Description of the action. |
||
297 | */ |
||
298 | public function getDescription() { |
||
299 | if (isset($this->description)) { |
||
300 | return $this->description; |
||
301 | } |
||
302 | return dt("Run the @short action.", array('@short' => $this->short)); |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Validate that the action can be run. |
||
307 | * |
||
308 | * @return bool |
||
309 | * Whether it can. |
||
310 | */ |
||
311 | public function validate() { |
||
312 | return TRUE; |
||
313 | } |
||
314 | |||
315 | /** |
||
316 | * Run the task. |
||
317 | * |
||
318 | * @param array $state |
||
319 | * Persistent state array that actions can use to communicate with |
||
320 | * following actions. |
||
321 | * |
||
322 | * @return bool |
||
323 | * Success or failure. |
||
324 | */ |
||
325 | public function run(\ArrayObject $state) { |
||
0 ignored issues
–
show
introduced
by
![]() |
|||
326 | return FALSE; |
||
327 | } |
||
328 | |||
329 | /** |
||
330 | * Roll back the task. |
||
331 | */ |
||
332 | public function rollback() { |
||
333 | return 'no rollback'; |
||
334 | } |
||
335 | |||
336 | /** |
||
337 | * Whether this action is enabled. |
||
338 | */ |
||
339 | public function enabled() { |
||
340 | // Incomplete actions are always disabled. |
||
341 | if ($this->short == "incompletely implemented") { |
||
342 | return FALSE; |
||
343 | } |
||
344 | // Disable if there is a killswitch and it is set. |
||
345 | if (isset($this->killSwitch) && drush_get_option($this->killSwitch, FALSE)) { |
||
346 | return FALSE; |
||
347 | } |
||
348 | // If there is an enable switch, let that decide. |
||
349 | if (isset($this->enableSwitch)) { |
||
350 | return drush_get_option($this->enableSwitch, FALSE); |
||
351 | } |
||
352 | // Else default to enabled. |
||
353 | return TRUE; |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Execute a sh command. |
||
358 | * |
||
359 | * Is run on the site. |
||
360 | * |
||
361 | * @param string $command |
||
362 | * Command to run. |
||
363 | * |
||
364 | * @return bool |
||
365 | * Whether the command succeeded. |
||
366 | */ |
||
367 | protected function sh($command) { |
||
368 | $exec = $command; |
||
369 | // Check that there is a remote host before trying to construct a remote |
||
370 | // command. |
||
371 | $host = drush_remote_host($this->site); |
||
372 | if (!empty($host)) { |
||
373 | $exec = drush_shell_proc_build($this->site, $command, TRUE); |
||
374 | } |
||
375 | else { |
||
376 | // Else just cd to the root of the alias. This allows us to test the |
||
377 | // code without a remote host. |
||
378 | $exec = "cd " . drush_escapeshellarg($this->site['root']) . " && " . $exec; |
||
379 | } |
||
380 | return $this->shLocal($exec); |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Get the output of the most recent command. |
||
385 | * |
||
386 | * @return string |
||
387 | * The output. |
||
388 | */ |
||
389 | protected function shOutput() { |
||
390 | return implode("\n", $this->shOutputArray()); |
||
391 | } |
||
392 | |||
393 | /** |
||
394 | * Get the output of the most recent command as an array. |
||
395 | * |
||
396 | * @return array |
||
397 | * Lines of output. |
||
398 | */ |
||
399 | protected function shOutputArray() { |
||
400 | return $this->shOutput; |
||
401 | } |
||
402 | |||
403 | /** |
||
404 | * Get the return code of the most recent command. |
||
405 | */ |
||
406 | protected function shRc() { |
||
407 | return $this->shRc; |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * Execute a sh locally. |
||
412 | * |
||
413 | * Works much like drush_shell_exec, however it captures the command return |
||
414 | * code too, and doesn't support interactive mode. |
||
415 | * |
||
416 | * @param string $command |
||
417 | * Command to run. |
||
418 | * |
||
419 | * @return bool |
||
420 | * Whether the command succeeded. |
||
421 | */ |
||
422 | protected function shLocal($command) { |
||
423 | $this->shOutput = array(); |
||
424 | $this->shRc = 0; |
||
425 | $args = func_get_args(); |
||
426 | // Escape args, but not the command. |
||
427 | for ($x = 1; $x < count($args); $x++) { |
||
428 | $args[$x] = drush_escapeshellarg($args[$x]); |
||
429 | } |
||
430 | // Mimic drush_shell_exec(), which can take a single or multiple args. |
||
431 | if (count($args) == 1) { |
||
432 | $command = $args[0]; |
||
433 | } |
||
434 | else { |
||
435 | $command = call_user_func_array('sprintf', $args); |
||
436 | } |
||
437 | |||
438 | if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) { |
||
439 | drush_print('Executing: ' . $command, 0, STDERR); |
||
440 | } |
||
441 | if (!drush_get_context('DRUSH_SIMULATE')) { |
||
442 | exec($command . ' 2>&1', $this->shOutput, $this->shRc); |
||
443 | |||
444 | if (drush_get_context('DRUSH_DEBUG')) { |
||
445 | foreach ($this->shOutput as $line) { |
||
446 | drush_print($line, 2); |
||
447 | } |
||
448 | } |
||
449 | } |
||
450 | |||
451 | // Exit code 0 means success. |
||
452 | return ($this->shRc == 0); |
||
453 | } |
||
454 | |||
455 | /** |
||
456 | * Find the tag pointed to by a SHA. |
||
457 | * |
||
458 | * @param string $sha |
||
459 | * The SHA to lookup. |
||
460 | * |
||
461 | * @return bool |
||
462 | * Whether the command succeeded. |
||
463 | */ |
||
464 | protected function gitPointsAt ($sha) { |
||
0 ignored issues
–
show
|
|||
465 | $success = $this->shLocal('git tag --points-at ' . $sha); |
||
466 | |||
467 | // If it failed we try a fallback command for older versions of |
||
468 | // git. |
||
469 | if (!$success) { |
||
470 | $success = $this->shLocal('git show-ref --tags -d | grep ^' . $sha . ' | sed -e \'s,.* refs/tags/,,\' -e \'s/\^{}//\''); |
||
0 ignored issues
–
show
|
|||
471 | } |
||
472 | |||
473 | return $success; |
||
474 | } |
||
475 | |||
476 | /** |
||
477 | * Execute a drush command. |
||
478 | * |
||
479 | * Is run on the site. |
||
480 | * |
||
481 | * @param string $command |
||
482 | * Command to run. |
||
483 | * @param string $args |
||
484 | * Command arguments. |
||
485 | * @param string $options |
||
486 | * Command arguments. |
||
487 | * |
||
488 | * @return bool |
||
489 | * Whether the command succeeded. |
||
490 | */ |
||
491 | protected function drush($command, $args = array(), $options = array()) { |
||
492 | $this->drushResult = drush_invoke_process($this->site, $command, $args, $options, TRUE); |
||
493 | if (!$this->drushResult || $this->drushResult['error_status'] != 0) { |
||
494 | return FALSE; |
||
495 | } |
||
496 | return TRUE; |
||
497 | } |
||
498 | |||
499 | /** |
||
500 | * Get the SHA to deploy. |
||
501 | * |
||
502 | * This is based on options from configuration or command. |
||
503 | * |
||
504 | * @return string |
||
505 | * The SHA to deploy. |
||
506 | */ |
||
507 | protected function pickSha($options = array()) { |
||
508 | $options = array( |
||
509 | 'branch' => drush_get_option('branch', NULL), |
||
510 | 'tag' => drush_get_option('tag', NULL), |
||
511 | 'sha' => drush_get_option('sha', NULL), |
||
512 | ); |
||
513 | |||
514 | $branch = $options['branch']; |
||
515 | $tag = $options['tag']; |
||
516 | $sha = $options['sha']; |
||
517 | |||
518 | $posibilities = array_filter(array($branch, $tag, $sha)); |
||
519 | if (count($posibilities) > 1) { |
||
520 | drush_log(dt('More than one of branch/tag/sha specified, using @selected.', array('@selected' => !empty($sha) ? 'sha' : 'tag')), 'warning'); |
||
521 | } |
||
522 | elseif (count($posibilities) < 1) { |
||
523 | return drush_set_error(dt('You must provide at least one of --branch, --tag or --sha.')); |
||
524 | } |
||
525 | |||
526 | // Use rev-list to ensure we get a full SHA rather than branch/tag/partial |
||
527 | // SHA. Ensures the existence of the branch/tag/SHA, and transforms |
||
528 | // annotated tag SHAs to the commit they're based on (so the repo will |
||
529 | // actually have the SHA we asked for checked out). |
||
530 | if ($this->shLocal('git rev-list -1 ' . end($posibilities) . ' --')) { |
||
531 | $sha = trim($this->shOutput()); |
||
532 | } |
||
533 | else { |
||
534 | if (!empty($sha)) { |
||
535 | return drush_set_error(dt('Unknown SHA.')); |
||
536 | } |
||
537 | else { |
||
538 | return drush_set_error(dt('Error finding SHA for ' . (!empty($branch) ? 'branch' : 'tag') . '.')); |
||
539 | } |
||
540 | } |
||
541 | |||
542 | return $sha; |
||
543 | } |
||
544 | |||
545 | /** |
||
546 | * Get the current git head revision. |
||
547 | * |
||
548 | * @param bool $no_cache |
||
549 | * Dont't use cache, but fetch anyway. |
||
550 | */ |
||
551 | protected function getHead($no_cache = FALSE) { |
||
552 | $name = $this->site['#name']; |
||
553 | if (!isset(self::$headShas[$name]) || $no_cache) { |
||
554 | self::$headShas[$name] = ''; |
||
555 | if ($this->sh('git rev-parse HEAD')) { |
||
556 | $sha = trim($this->shOutput()); |
||
557 | // Confirm that we can see the HEAD locally. |
||
558 | if ($this->shLocal('git show -s ' . $sha)) { |
||
559 | self::$headShas[$name] = $sha; |
||
560 | } |
||
561 | else { |
||
562 | drush_log(dt("Could not find the deployed HEAD (@sha) locally, you pulled recently?", array('@sha' => $sha)), 'warning'); |
||
563 | } |
||
564 | } |
||
565 | } |
||
566 | return self::$headShas[$name]; |
||
567 | } |
||
568 | } |
||
569 | } |
||
570 | |||
571 | /** |
||
572 | * Use a namespace to keep actions together. |
||
573 | */ |
||
574 | namespace Deployotron\Actions { |
||
575 | use Deployotron\Action; |
||
576 | |||
577 | /** |
||
578 | * Generic command action. |
||
579 | * |
||
580 | * Automatically instantiated from pre/post-* options. |
||
581 | */ |
||
582 | class CommandAction extends Action { |
||
583 | /** |
||
584 | * Create action. |
||
585 | */ |
||
586 | public function __construct($site, $command) { |
||
587 | $this->description = 'Run command: ' . $command; |
||
588 | $this->runMessage = 'Running command: ' . $command; |
||
589 | $this->short = 'run ' . $command; |
||
590 | |||
591 | $this->command = $command; |
||
592 | parent::__construct($site); |
||
593 | } |
||
594 | |||
595 | /** |
||
596 | * {@inheritdoc} |
||
597 | */ |
||
598 | public function run(\ArrayObject $state) { |
||
599 | $success = $this->sh($this->command); |
||
600 | drush_print($this->shOutput()); |
||
601 | if (!$success) { |
||
602 | return drush_set_error(dt('Error running command "@command"', array('@command' => $this->command))); |
||
603 | } |
||
604 | return TRUE; |
||
605 | } |
||
606 | } |
||
607 | |||
608 | /** |
||
609 | * Sanity check. |
||
610 | * |
||
611 | * Check for locally modified files and do a dry-run checkout. |
||
612 | */ |
||
613 | class SanityCheck extends Action { |
||
614 | protected $runMessage = 'Fetching and sanity checking'; |
||
615 | protected $short = 'sanity check'; |
||
616 | protected $help = 'Fetches the new code and checks that the repository is clean.'; |
||
617 | protected $options = array( |
||
618 | 'insanity' => "Don't check that the server repository is clean. Might cause data loss.", |
||
619 | ); |
||
620 | |||
621 | /** |
||
622 | * {@inheritdoc} |
||
623 | */ |
||
624 | public function getDescription() { |
||
625 | if (drush_get_option('insanity', FALSE)) { |
||
626 | return COLOR_YELLOW . dt('Skipping sanity check of deployed repo. Might cause data loss or Git failures.') . COLOR_RESET; |
||
627 | } |
||
628 | |||
629 | return dt('Run git fetch and check that the deployed git repository is clean.'); |
||
630 | } |
||
631 | |||
632 | /** |
||
633 | * {@inheritdoc} |
||
634 | */ |
||
635 | public function validate() { |
||
636 | if ($this->sha = $this->pickSha()) { |
||
637 | return TRUE; |
||
638 | } |
||
639 | return FALSE; |
||
640 | } |
||
641 | |||
642 | /** |
||
643 | * {@inheritdoc} |
||
644 | */ |
||
645 | public function run(\ArrayObject $state) { |
||
646 | // Fetch the latest changes from upstream. |
||
647 | if (!$this->sh('git fetch')) { |
||
648 | $message = $this->shOutput(); |
||
649 | if (preg_match('/Permission denied \(publickey\)/', $message)) { |
||
650 | drush_log(dt('Access denied to repository URL.'), 'error'); |
||
651 | drush_log(dt('Ensure that either that the user on the server has access to the repository, or use "ForwardAgent yes" in .ssh/config.'), 'error'); |
||
652 | } |
||
653 | else { |
||
654 | drush_print($message); |
||
655 | } |
||
656 | return drush_set_error(dt("Could not fetch from remote repository.")); |
||
657 | } |
||
658 | |||
659 | // Check that we can see the sha we want to deploy. |
||
660 | if (!$this->sh('git log -1 ' . $this->sha . ' --')) { |
||
661 | $message = $this->shOutput(); |
||
662 | if (preg_match('/^fatal: bad object/', $message)) { |
||
663 | return drush_set_error(dt('Could not find SHA, did you forget to push?')); |
||
664 | } |
||
665 | drush_print($message); |
||
666 | return drush_set_error(dt('Could not find SHA remotely.')); |
||
667 | } |
||
668 | |||
669 | if (drush_get_option('insanity', FALSE)) { |
||
670 | drush_log(dt('Skipping sanity check of deployed repo.'), 'warning'); |
||
671 | return TRUE; |
||
672 | } |
||
673 | $fallback = FALSE; |
||
674 | // http://stackoverflow.com/questions/3878624/how-do-i-programmatically-determine-if-there-are-uncommited-changes |
||
0 ignored issues
–
show
|
|||
675 | // claims this is the proper way. I'm inclined to agree. |
||
676 | // However, these are newer additions to Git, so fallback to git status |
||
677 | // if they fail. |
||
678 | if (!$this->sh('git diff-files --quiet --ignore-submodules --')) { |
||
679 | if ($this->shRc() != 129) { |
||
680 | return drush_set_error(dt('Remote git checkout not clean.')); |
||
681 | } |
||
682 | $fallback = TRUE; |
||
683 | } |
||
684 | if (!$fallback) { |
||
685 | if (!$this->sh('git diff-index --cached --quiet HEAD --ignore-submodules --')) { |
||
686 | if ($this->shRc() != 129) { |
||
687 | return drush_set_error(dt('Uncommitted changes in the index.')); |
||
688 | } |
||
689 | $fallback = TRUE; |
||
690 | } |
||
691 | } |
||
692 | if ($fallback) { |
||
693 | if (!$this->sh('git status -s --untracked-files=no')) { |
||
694 | return drush_set_error(dt('Error running git status -s --untracked-files=no.')); |
||
695 | } |
||
696 | $output = trim($this->shOutput()); |
||
697 | if (!empty($output)) { |
||
698 | return drush_set_error(dt('Remote git checkout not clean.')); |
||
699 | } |
||
700 | } |
||
701 | |||
702 | return TRUE; |
||
703 | } |
||
704 | } |
||
705 | |||
706 | /** |
||
707 | * Deploy code. |
||
708 | */ |
||
709 | class DeployCode extends Action { |
||
710 | protected $runMessage = 'Deploying'; |
||
711 | protected $killSwitch = 'no-deploy'; |
||
712 | protected $short = 'deploy code'; |
||
713 | protected $help = 'Checks out a specified branch/tag/sha on the site.'; |
||
714 | protected $options = array( |
||
715 | 'branch' => 'Branch to check out.', |
||
716 | 'tag' => 'Tag to check out.', |
||
717 | 'sha' => 'SHA to check out.', |
||
718 | ); |
||
719 | |||
720 | /** |
||
721 | * {@inheritdoc} |
||
722 | */ |
||
723 | public function getDescription() { |
||
724 | $head = $this->getHead(); |
||
725 | if (!$head) { |
||
726 | return COLOR_YELLOW . dt("Cannot find the deployed HEAD.") . COLOR_RESET; |
||
727 | } |
||
728 | |||
729 | // Collect info and log entry for the commit we're deploying. |
||
730 | $this->shLocal('git log --pretty="format:%an <%ae> @ %ci (%cr)%n%n%B" --color --no-walk ' . $this->sha); |
||
731 | $commit_info = $this->shOutput(); |
||
732 | |||
733 | // Collect info and log entry for the deployed head. |
||
734 | $this->shLocal('git log --pretty="format:%an <%ae> @ %ci (%cr)%n%n%B" --color --no-walk ' . $head); |
||
735 | $head_info = $this->shOutput(); |
||
736 | |||
737 | // Create diffstat between the deployed commit and the one we're |
||
738 | // deploying. |
||
739 | $this->shLocal('git diff --color --stat ' . $head . ' ' . $this->sha); |
||
740 | $stat = $this->shOutput(); |
||
741 | $stat = explode("\n", $stat); |
||
742 | if (count($stat) > 20) { |
||
743 | $stat = array_merge(array_slice($stat, 0, 8), array('', ' ...', ''), array_slice($stat, -8)); |
||
744 | } |
||
745 | |||
746 | $desc = dt("Check out @sha:\n", array('@sha' => $this->sha)); |
||
747 | $desc .= $commit_info . "\n\n"; |
||
748 | |||
749 | $desc .= dt("Currently deployed: @sha\n", array('@sha' => $head)); |
||
750 | $desc .= $head_info . "\n\n"; |
||
751 | |||
752 | $desc .= dt("All changes:\n!changes", array('!changes' => implode("\n", $stat))); |
||
753 | |||
754 | return $desc; |
||
755 | } |
||
756 | |||
757 | /** |
||
758 | * {@inheritdoc} |
||
759 | */ |
||
760 | public function validate() { |
||
761 | if ($this->sha = $this->pickSha()) { |
||
762 | return TRUE; |
||
763 | } |
||
764 | return FALSE; |
||
765 | } |
||
766 | |||
767 | /** |
||
768 | * {@inheritdoc} |
||
769 | */ |
||
770 | public function run(\ArrayObject $state) { |
||
771 | // Some useful information for other actions. |
||
772 | $state['requested_branch'] = drush_get_option('branch', NULL); |
||
773 | $state['requested_tag'] = drush_get_option('tag', NULL); |
||
774 | $state['requested_sha'] = drush_get_option('sha', NULL); |
||
775 | |||
776 | if (!$this->sh('git checkout ' . $this->sha)) { |
||
777 | drush_print($this->shOutput()); |
||
778 | return drush_set_error(dt('Could not checkout code.')); |
||
779 | } |
||
780 | |||
781 | // An extra safety check to make sure that things are as we think. |
||
782 | $deployed_sha = $this->getHead(TRUE); |
||
783 | if ($deployed_sha) { |
||
784 | if ($deployed_sha != $this->sha) { |
||
785 | return drush_set_error(dt('Code not properly deployed, head is at @sha now.', array('@sha' => $deployed_sha))); |
||
786 | } |
||
787 | else { |
||
788 | drush_log(dt('HEAD now at @sha', array('@sha' => $deployed_sha)), 'status'); |
||
789 | $state['deployed_sha'] = $deployed_sha; |
||
790 | } |
||
791 | } |
||
792 | else { |
||
793 | drush_print($this->shOutput()); |
||
794 | return drush_set_error(dt('Error confirming that the code update succceded.')); |
||
795 | } |
||
796 | |||
797 | return TRUE; |
||
798 | } |
||
799 | } |
||
800 | |||
801 | /** |
||
802 | * Set site offline. |
||
803 | */ |
||
804 | class SiteOffline extends Action { |
||
805 | protected $description = 'Set the site offline.'; |
||
806 | protected $runMessage = 'Setting site offline'; |
||
807 | protected $killSwitch = 'no-offline'; |
||
808 | protected $short = 'set site offline'; |
||
809 | |||
810 | /** |
||
811 | * {@inheritdoc} |
||
812 | */ |
||
813 | public function run(\ArrayObject $state) { |
||
814 | if (!$this->drush('variable-set', array('maintenance_mode', 1))) { |
||
815 | return drush_set_error(dt('Error setting site offline.')); |
||
816 | } |
||
817 | return TRUE; |
||
818 | } |
||
819 | |||
820 | /** |
||
821 | * {@inheritdoc} |
||
822 | */ |
||
823 | public function rollback() { |
||
824 | // Use the online action as rollback. |
||
825 | $online = new SiteOnline($this->site); |
||
826 | $online->run(new \ArrayObject()); |
||
827 | } |
||
828 | } |
||
829 | |||
830 | /** |
||
831 | * Set site online. |
||
832 | */ |
||
833 | class SiteOnline extends Action { |
||
834 | protected $description = 'Set the site online.'; |
||
835 | protected $runMessage = 'Setting site online'; |
||
836 | protected $killSwitch = 'no-offline'; |
||
837 | protected $short = 'set site online'; |
||
838 | protected $switchSuffix = 'online'; |
||
839 | |||
840 | /** |
||
841 | * {@inheritdoc} |
||
842 | */ |
||
843 | public function run(\ArrayObject $state) { |
||
844 | if (!$this->drush('variable-set', array('maintenance_mode', 0))) { |
||
845 | return drush_set_error(dt('Error setting site online.')); |
||
846 | } |
||
847 | return TRUE; |
||
848 | } |
||
849 | |||
850 | /** |
||
851 | * {@inheritdoc} |
||
852 | */ |
||
853 | public function rollback() { |
||
854 | // Use the online action as rollback. |
||
855 | $online = new SiteOffline($this->site); |
||
856 | $online->run(new \ArrayObject()); |
||
857 | } |
||
858 | } |
||
859 | |||
860 | /** |
||
861 | * Backup database. |
||
862 | */ |
||
863 | class BackupDatabase extends Action { |
||
864 | protected $runMessage = 'Dumping database'; |
||
865 | protected $killSwitch = 'no-dump'; |
||
866 | protected $short = 'dump database'; |
||
867 | protected $help = 'Makes a SQL dump of the site database.'; |
||
868 | protected $options = array( |
||
869 | 'dump-dir' => array( |
||
870 | 'description' => 'Directory for database dumps, defaults to /tmp.', |
||
871 | 'example-value' => 'path', |
||
872 | ), |
||
873 | ); |
||
874 | |||
875 | /** |
||
876 | * Get the name of a dump. |
||
877 | */ |
||
878 | public static function filename($alias, $date_str, $sha) { |
||
879 | return sprintf("%s/deploy.%s.%s.%s.sql", drush_get_option('dump-dir', '/tmp'), $alias, $date_str, $sha); |
||
880 | } |
||
881 | |||
882 | /** |
||
883 | * {@inheritdoc} |
||
884 | */ |
||
885 | public function getDescription() { |
||
886 | return dt("Dump database to @file.", array('@file' => $this->dumpFilename())); |
||
887 | } |
||
888 | |||
889 | /** |
||
890 | * {@inheritdoc} |
||
891 | */ |
||
892 | public function run(\ArrayObject $state) { |
||
893 | if (!$this->drush('sql-dump', array(), array('no-ordered-dump' => TRUE, 'result-file' => $this->dumpFilename()))) { |
||
894 | return drush_set_error('Error dumping database.'); |
||
895 | } |
||
896 | $state['database_dump'] = $this->dumpFilename(); |
||
897 | return TRUE; |
||
898 | } |
||
899 | |||
900 | /** |
||
901 | * Figure out dump filename. |
||
902 | */ |
||
903 | protected function dumpFilename() { |
||
904 | // Because there can pass time between this is called first and last |
||
905 | // (--confirm primarily). |
||
906 | static $date; |
||
907 | if (!$date) { |
||
908 | $date = date('Y-m-d\TH:i:s'); |
||
909 | } |
||
910 | |||
911 | return static::filename($this->site['#name'], $date, $this->getHead()); |
||
912 | } |
||
913 | } |
||
914 | |||
915 | /** |
||
916 | * Restore database. |
||
917 | */ |
||
918 | class RestoreDatabase extends Action { |
||
919 | protected $runMessage = 'Restoring database'; |
||
920 | protected $short = 'restore database'; |
||
921 | protected $options = array( |
||
922 | 'file' => array( |
||
923 | 'description' => 'Database dump file to restore.', |
||
924 | 'example-value' => 'filename', |
||
925 | ), |
||
926 | ); |
||
927 | |||
928 | /** |
||
929 | * {@inheritdoc} |
||
930 | */ |
||
931 | public function validate() { |
||
932 | if (!drush_get_option('file', NULL)) { |
||
933 | return drush_set_error(dt('Missing file.')); |
||
934 | } |
||
935 | |||
936 | return TRUE; |
||
937 | } |
||
938 | |||
939 | /** |
||
940 | * {@inheritdoc} |
||
941 | */ |
||
942 | public function getDescription() { |
||
943 | return dt("Restore database from @file.", array('@file' => drush_get_option('file', NULL))); |
||
944 | } |
||
945 | |||
946 | /** |
||
947 | * {@inheritdoc} |
||
948 | */ |
||
949 | public function run(\ArrayObject $state) { |
||
950 | if (!$this->drush('sql-connect', array(), array('pipe' => TRUE))) { |
||
951 | return drush_set_error('Error getting database connection setup.'); |
||
952 | } |
||
953 | |||
954 | if (!is_string($this->drushResult['object'])) { |
||
955 | return drush_set_error('Weird result from sql-connnect.'); |
||
956 | } |
||
957 | |||
958 | $command = $this->drushResult['object']; |
||
959 | |||
960 | if (!$this->sh($command . " < " . drush_get_option('file', NULL))) { |
||
961 | drush_print($this->shOutput()); |
||
962 | return drush_set_error('Error restoring database.'); |
||
963 | } |
||
964 | |||
965 | return TRUE; |
||
966 | } |
||
967 | } |
||
968 | |||
969 | /** |
||
970 | * Purge old backups. |
||
971 | */ |
||
972 | class PurgeDatabaseBackups extends Action { |
||
973 | protected $runMessage = 'Purging old database dumps.'; |
||
974 | protected $short = 'purge old dumps'; |
||
975 | protected $options = array( |
||
976 | 'num-dumps' => array( |
||
977 | 'description' => 'Number of database dumps to keep. 0 for unlimited.', |
||
978 | 'example-value' => '5', |
||
979 | ), |
||
980 | ); |
||
981 | protected $switchSuffix = 'purge'; |
||
982 | |||
983 | /** |
||
984 | * Dumps to delete. |
||
985 | */ |
||
986 | protected $deleteDumps = array(); |
||
987 | |||
988 | /** |
||
989 | * {@inheritdoc} |
||
990 | */ |
||
991 | public function validate() { |
||
992 | $dumping = TRUE; |
||
993 | if (drush_get_option('no-dump', FALSE)) { |
||
994 | $dumping = FALSE; |
||
995 | } |
||
996 | $max_dumps = drush_get_option('num-dumps', 5); |
||
997 | |||
998 | if ($max_dumps > 0) { |
||
999 | // If we're dumping a new dump, we need to keep one less than the max to |
||
1000 | // make room for the new one. |
||
1001 | $keep = $max_dumps - ($dumping ? 1 : 0); |
||
1002 | $this->sh('ls ' . BackupDatabase::filename($this->site['#name'], '*', '*')); |
||
1003 | $dumps = $this->shOutputArray(); |
||
1004 | if (count($dumps) > $keep) { |
||
1005 | // Reverse sort to get the newest first. |
||
1006 | rsort($dumps); |
||
1007 | $this->deleteDumps = array_slice($dumps, $keep); |
||
1008 | } |
||
1009 | } |
||
1010 | |||
1011 | return TRUE; |
||
1012 | } |
||
1013 | |||
1014 | /** |
||
1015 | * {@inheritdoc} |
||
1016 | */ |
||
1017 | public function getDescription() { |
||
1018 | if (count($this->deleteDumps)) { |
||
1019 | return dt("Purge the following dump files:\n@files.", array('@files' => implode("\n", $this->deleteDumps))); |
||
1020 | |||
1021 | } |
||
1022 | return dt("Not purging any dumps."); |
||
1023 | } |
||
1024 | |||
1025 | /** |
||
1026 | * {@inheritdoc} |
||
1027 | */ |
||
1028 | public function run(\ArrayObject $state) { |
||
1029 | if (count($this->deleteDumps)) { |
||
1030 | $this->sh('rm ' . implode(" ", array_map('drush_escapeshellarg', $this->deleteDumps))); |
||
1031 | } |
||
1032 | |||
1033 | // We don't consider failure to delete dumps serious enough to fail the |
||
1034 | // deployment. |
||
1035 | return TRUE; |
||
1036 | } |
||
1037 | } |
||
1038 | |||
1039 | /** |
||
1040 | * Update database. |
||
1041 | */ |
||
1042 | class UpdateDatabase extends Action { |
||
1043 | protected $description = 'Runs database updates (as with update.php).'; |
||
1044 | protected $runMessage = 'Running database updates'; |
||
1045 | protected $killSwitch = 'no-updb'; |
||
1046 | protected $short = 'update database schema'; |
||
1047 | |||
1048 | /** |
||
1049 | * {@inheritdoc} |
||
1050 | */ |
||
1051 | public function run(\ArrayObject $state) { |
||
1052 | if (!$this->drush('updb', array(), array('yes' => TRUE))) { |
||
1053 | return drush_set_error(dt('Error running database updates.')); |
||
1054 | } |
||
1055 | return TRUE; |
||
1056 | } |
||
1057 | } |
||
1058 | |||
1059 | /** |
||
1060 | * Clear cache. |
||
1061 | */ |
||
1062 | class ClearCache extends Action { |
||
1063 | protected $description = 'Clear all Drupal caches.'; |
||
1064 | protected $runMessage = 'Clearing caches'; |
||
1065 | protected $short = 'cache clear'; |
||
1066 | protected $killSwitch = 'no-cc-all'; |
||
1067 | |||
1068 | /** |
||
1069 | * {@inheritdoc} |
||
1070 | */ |
||
1071 | public function run(\ArrayObject $state) { |
||
1072 | if (!$this->drush('cc', array('all'), array())) { |
||
1073 | return drush_set_error(dt('Error clearing cache.')); |
||
1074 | } |
||
1075 | return TRUE; |
||
1076 | } |
||
1077 | } |
||
1078 | |||
1079 | /** |
||
1080 | * Prepare OMG. |
||
1081 | */ |
||
1082 | class OMGPrepare extends Action { |
||
1083 | protected $description = 'Prepare restore.'; |
||
1084 | protected $runMessage = 'Preparing'; |
||
1085 | protected $short = 'preparing'; |
||
1086 | protected $help = 'Prepares restoring by looking for dumps to import.'; |
||
1087 | protected $options = array( |
||
1088 | 'dump-dir' => array( |
||
1089 | 'description' => 'Directory for database dumps.', |
||
1090 | 'example-value' => 'path', |
||
1091 | ), |
||
1092 | ); |
||
1093 | protected $switchSuffix = 'prepare'; |
||
1094 | |||
1095 | /** |
||
1096 | * {@inheritdoc} |
||
1097 | */ |
||
1098 | public function validate() { |
||
1099 | // Try to find some dumps and give them as options for restoring. |
||
1100 | $regex = '{^deploy\.' . preg_quote($this->site['#name']) . '\.(\d+-\d+-\d+T\d+:\d+:\d+)\.([0-9a-f]+)\.sql$}'; |
||
1101 | $this->sh('ls ' . drush_get_option('dump-dir', '/tmp')); |
||
1102 | $listing = $this->shOutput(); |
||
1103 | $dumps = array(); |
||
1104 | foreach (array_reverse(explode("\n", $listing)) as $line) { |
||
1105 | if (preg_match($regex, $line, $matches)) { |
||
1106 | $dumps[$matches[1]] = $matches[2]; |
||
1107 | } |
||
1108 | } |
||
1109 | |||
1110 | if (!empty($dumps)) { |
||
1111 | $date = drush_choice($dumps, dt('Please select a dump.'), '!key (!value)'); |
||
1112 | if ($date) { |
||
1113 | $sha = $dumps[$date]; |
||
1114 | $file = 'deploy.' . $this->site['#name'] . '.' . $date . '.' . $sha . '.sql'; |
||
1115 | // We simply set the options so the other actions will see them. The |
||
1116 | // DeployCode action will check that the SHA is available locally |
||
1117 | // before validating, so we'll avoid the worst if dumps get mixed up. |
||
1118 | drush_set_option('sha', $sha); |
||
1119 | drush_set_option('file', drush_get_option('dump-dir', '/tmp') . '/' . $file); |
||
1120 | return TRUE; |
||
1121 | } |
||
1122 | else { |
||
1123 | return drush_set_error(dt('Aborting.')); |
||
1124 | } |
||
1125 | } |
||
1126 | else { |
||
1127 | return drush_set_error(dt('No database dumps found.')); |
||
1128 | } |
||
1129 | } |
||
1130 | |||
1131 | /** |
||
1132 | * {@inheritdoc} |
||
1133 | */ |
||
1134 | public function run(\ArrayObject $state) { |
||
1135 | // Doing nothing. |
||
1136 | return TRUE; |
||
1137 | } |
||
1138 | } |
||
1139 | |||
1140 | /** |
||
1141 | * Create a VERSION.txt file. |
||
1142 | */ |
||
1143 | class CreateVersionTxt extends Action { |
||
1144 | protected $description = 'Create VERSION.txt.'; |
||
1145 | protected $runMessage = 'Creating VERSION.txt'; |
||
1146 | protected $short = 'create VERSION.txt'; |
||
1147 | protected $killSwitch = 'no-create-version-txt'; |
||
1148 | protected $switchSuffix = 'version-txt'; |
||
1149 | |||
1150 | /** |
||
1151 | * {@inheritdoc} |
||
1152 | */ |
||
1153 | public function run(\ArrayObject $state) { |
||
0 ignored issues
–
show
run uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
1154 | if (!empty($state['deployed_sha'])) { |
||
1155 | // Ask git which tags points to this commit. |
||
1156 | $this->gitPointsAt($state['deployed_sha']); |
||
1157 | $tags = implode(', ', $this->shOutputArray()); |
||
1158 | |||
1159 | $version_txt = array(); |
||
1160 | $version_txt[] = 'Deployment info'; |
||
1161 | $version_txt[] = '---------------'; |
||
1162 | if (!empty($state['requested_branch'])) { |
||
1163 | $version_txt[] = 'Branch: ' . $state['requested_branch']; |
||
1164 | } |
||
1165 | if (!empty($tags)) { |
||
1166 | $version_txt[] = 'Tags: ' . $tags; |
||
1167 | } |
||
1168 | $version_txt[] = 'SHA: ' . $state['deployed_sha']; |
||
1169 | $version_txt[] = 'Time of deployment: ' . date('r'); |
||
1170 | $version_txt[] = 'Deployer: ' . $_SERVER['USER'] . '@' . php_uname('n'); |
||
1171 | |||
1172 | // Delete any pre-existing VERSION.txt file and create a new one. |
||
1173 | $this->sh('rm VERSION.txt'); |
||
1174 | // You'd think that echo would do the job, but it's not consistent |
||
1175 | // across shells. Bash and most /bin/echo requires the -n option to |
||
1176 | // expand \n to a newline, while /bin/sh built-in echo doesn't and |
||
1177 | // prints the -n as part of the output. But printf works the same and is |
||
1178 | // POSIX 7, which should cover our bases. |
||
1179 | $this->sh('printf ' . escapeshellarg(implode("\\n", $version_txt) . "\\n") . ' > VERSION.txt'); |
||
1180 | } |
||
1181 | else { |
||
1182 | drush_log(dt('No version deployed, not creating/updating VERSION.txt.'), 'warning'); |
||
1183 | } |
||
1184 | return TRUE; |
||
1185 | } |
||
1186 | } |
||
1187 | |||
1188 | /** |
||
1189 | * Send a notification to Flowdock. |
||
1190 | */ |
||
1191 | class FlowdockNotificaton extends Action { |
||
1192 | protected $description = 'Send Flowdock notification.'; |
||
1193 | protected $runMessage = 'Sending Flowdock notification.'; |
||
1194 | protected $short = 'send Flowdock notification'; |
||
1195 | protected $enableSwitch = 'flowdock-token'; |
||
1196 | protected $options = array( |
||
1197 | 'flowdock-token' => array( |
||
1198 | 'description' => 'Flowdock token.', |
||
1199 | 'example-value' => 'token', |
||
1200 | ), |
||
1201 | ); |
||
1202 | protected $switchSuffix = 'flowdock'; |
||
1203 | |||
1204 | /** |
||
1205 | * {@inheritdoc} |
||
1206 | */ |
||
1207 | public function run(\ArrayObject $state) { |
||
1208 | if (!empty($state['deployed_sha'])) { |
||
1209 | $this->gitPointsAt($state['deployed_sha']); |
||
1210 | $tags = implode(', ', $this->shOutputArray()); |
||
1211 | |||
1212 | $subject = 'Deployment to ' . $this->site['remote-user'] . '@' . |
||
1213 | $this->site['remote-host'] . ':' . $this->site['root']; |
||
1214 | |||
1215 | $body = 'SHA: ' . $state['deployed_sha'] . |
||
1216 | (!empty($state['requested_branch']) ? '<br />Branch: ' . $state['requested_branch'] : '') . |
||
1217 | (!empty($tags) ? '<br />Tags: ' . $tags : ''); |
||
1218 | |||
1219 | $data = array( |
||
1220 | 'source' => 'Deployotron', |
||
1221 | 'from_address' => '[email protected]', |
||
1222 | 'subject' => $subject, |
||
1223 | 'content' => $body, |
||
1224 | 'tags' => array('deployotron', $this->getFlowdockTag()), |
||
1225 | ); |
||
1226 | $data = json_encode($data); |
||
1227 | |||
1228 | $service_url = 'https://api.flowdock.com/v1/messages/team_inbox/' . drush_get_option('flowdock-token', ''); |
||
1229 | |||
1230 | $curl = curl_init($service_url); |
||
1231 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); |
||
1232 | curl_setopt($curl, CURLOPT_POST, TRUE); |
||
1233 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); |
||
1234 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( |
||
1235 | 'Content-Type: application/json', |
||
1236 | 'Content-Length: ' . strlen($data), |
||
1237 | ) |
||
1238 | ); |
||
1239 | |||
1240 | $curl_response = curl_exec($curl); |
||
1241 | if ($curl_response != '{}') { |
||
1242 | drush_log(dt('Unexpected response from Flowdock: !response', array('!response' => $curl_response)), 'warning'); |
||
1243 | } |
||
1244 | curl_close($curl); |
||
1245 | } |
||
1246 | else { |
||
1247 | drush_log(dt('No version deployed, not sending Flowdock notification.'), 'warning'); |
||
1248 | } |
||
1249 | return TRUE; |
||
1250 | } |
||
1251 | |||
1252 | /** |
||
1253 | * Get a Flowdock tag for the site. |
||
1254 | */ |
||
1255 | private function getFlowdockTag() { |
||
1256 | return preg_replace('/[^[:alnum:]_\-]/', '_', $this->site['#name']); |
||
1257 | } |
||
1258 | } |
||
1259 | |||
1260 | /** |
||
1261 | * Send a notification to New Relic. |
||
1262 | */ |
||
1263 | class NewRelicNotificaton extends Action { |
||
1264 | protected $description = 'Send New Relic notification.'; |
||
1265 | protected $runMessage = 'Sending New Relic notification.'; |
||
1266 | protected $short = 'send new relic notification'; |
||
1267 | protected $enableSwitch = 'newrelic-api-key'; |
||
1268 | protected $options = array( |
||
1269 | 'newrelic-app-name' => array( |
||
1270 | 'description' => 'New Relic application name.', |
||
1271 | 'example-value' => 'name', |
||
1272 | ), |
||
1273 | 'newrelic-app-id' => array( |
||
1274 | 'description' => 'New Relic application id.', |
||
1275 | 'example-value' => 'id', |
||
1276 | ), |
||
1277 | 'newrelic-api-key' => array( |
||
1278 | 'description' => 'New Relic API key.', |
||
1279 | 'example-value' => 'key', |
||
1280 | ), |
||
1281 | ); |
||
1282 | protected $switchSuffix = 'newrelic'; |
||
1283 | |||
1284 | /** |
||
1285 | * {@inheritdoc} |
||
1286 | */ |
||
1287 | public function validate() { |
||
1288 | if (!drush_get_option('newrelic-app-name', NULL) && !drush_get_option('newrelic-app-id', NULL)) { |
||
1289 | return drush_set_error('Need at least one of --newrelic-app-name or --newrelic-app-id'); |
||
1290 | } |
||
1291 | // The api-key must have been specified as its the enable switch. |
||
1292 | return TRUE; |
||
1293 | } |
||
1294 | |||
1295 | /** |
||
1296 | * {@inheritdoc} |
||
1297 | */ |
||
1298 | public function run(\ArrayObject $state) { |
||
0 ignored issues
–
show
run uses the super-global variable $_SERVER which is generally not recommended.
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: // Bad
class Router
{
public function generate($path)
{
return $_SERVER['HOST'].$path;
}
}
// Better
class Router
{
private $host;
public function __construct($host)
{
$this->host = $host;
}
public function generate($path)
{
return $this->host.$path;
}
}
class Controller
{
public function myAction(Request $request)
{
// Instead of
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
// Better (assuming you use the Symfony2 request)
$page = $request->query->get('page', 1);
}
}
![]() |
|||
1299 | if (!empty($state['deployed_sha'])) { |
||
1300 | $this->gitPointsAt($state['deployed_sha']); |
||
1301 | $tags = implode(', ', $this->shOutputArray()); |
||
1302 | |||
1303 | $body = 'SHA: ' . $state['deployed_sha'] . |
||
1304 | (!empty($state['requested_branch']) ? "\nBranch: " . $state['requested_branch'] : '') . |
||
1305 | (!empty($tags) ? "\nTags: " . $tags : ''); |
||
1306 | |||
1307 | $deployment = array( |
||
1308 | 'description' => $body, |
||
1309 | 'revision' => $state['deployed_sha'], |
||
1310 | // @todo 'changelog' => '', |
||
1311 | 'user' => $_SERVER['USER'] . '@' . php_uname('n'), |
||
1312 | ); |
||
1313 | |||
1314 | $app_name = drush_get_option('newrelic-app-name', NULL); |
||
1315 | if ($app_name) { |
||
1316 | $deployment['app_name'] = $app_name; |
||
1317 | } |
||
1318 | |||
1319 | $app_id = drush_get_option('newrelic-app-id', NULL); |
||
1320 | if ($app_id) { |
||
1321 | $deployment['application_id'] = $app_id; |
||
1322 | } |
||
1323 | |||
1324 | $data = http_build_query(array('deployment' => $deployment)); |
||
1325 | $service_url = 'https://api.newrelic.com/deployments.xml'; |
||
1326 | |||
1327 | $curl = curl_init($service_url); |
||
1328 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); |
||
1329 | curl_setopt($curl, CURLOPT_POST, TRUE); |
||
1330 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); |
||
1331 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( |
||
1332 | 'Content-Type: application/x-www-form-urlencoded', |
||
1333 | 'Content-Length: ' . strlen($data), |
||
1334 | 'x-api-key: ' . drush_get_option('newrelic-api-key'), |
||
1335 | ) |
||
1336 | ); |
||
1337 | |||
1338 | $curl_response = curl_exec($curl); |
||
1339 | if ($curl_response === FALSE) { |
||
1340 | drush_log(dt('Curl failed: !response', array('!response' => curl_error($curl))), 'error'); |
||
1341 | } |
||
1342 | elseif (!$curl_response) { |
||
1343 | drush_log(dt('New Relic notification failed for some reason.'), 'error'); |
||
1344 | } |
||
1345 | curl_close($curl); |
||
1346 | } |
||
1347 | else { |
||
1348 | drush_log(dt('No version deployed, not sending New Relic notification.'), 'warning'); |
||
1349 | } |
||
1350 | return TRUE; |
||
1351 | } |
||
1352 | } |
||
1353 | } |
||
1354 |