Completed
Pull Request — master (#518)
by Michael
03:07
created

FetchJob::onFailure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
class FetchJob extends DeploynautJob {
4
5
	public $args;
6
7
	/**
8
	 * @var DNProject
9
	 */
10
	protected $project;
11
12
	/**
13
	 * @var DeploynautLogFile
14
	 */
15
	protected $log;
16
17
	/**
18
	 * @var string
19
	 */
20
	protected $user;
21
22
	/**
23
	 * @global array $databaseConfig
24
	 */
25
	public function setUp() {
26
		global $databaseConfig;
27
		DB::connect($databaseConfig);
28
29
		$this->updateStatus('Started');
30
		chdir(BASE_PATH);
31
	}
32
33
	public function tearDown() {
34
		$this->updateStatus('Finished');
35
		chdir(BASE_PATH);
36
	}
37
38
	public function onFailure(Exception $exception) {
39
		$this->updateStatus('Failed');
40
	}
41
42
	public function perform() {
43
		set_time_limit(0);
44
45
		if(!empty($this->args['logfile'])) {
46
			$this->log = new DeploynautLogFile($this->args['logfile']);
47
		}
48
49
		$this->project = DNProject::get()->byId($this->args['projectID']);
50
		if(!($this->project && $this->project->exists())) {
51
			throw new RuntimeException(sprintf('Project ID %s not found', $this->args['projectID']));
52
		}
53
54
		$this->user = DNData::inst()->getGitUser() ?: null;
55
56
		// Disallow concurrent git fetches on the same project.
57
		// Only consider fetches started in the last 30 minutes (older jobs probably got stuck)
58
		try {
59
			if(!empty($this->args['fetchID'])) {
60
				$runningFetches = DNGitFetch::get()
61
					->filter(array(
62
						'ProjectID' => $this->project->ID,
63
						'Status' => array('Queued', 'Started'),
64
						'Created:GreaterThan' => strtotime('-30 minutes')
65
					))
66
					->exclude('ID', $this->args['fetchID']);
67
68
				if($runningFetches->count()) {
69
					$runningFetch = $runningFetches->first();
70
					$message = sprintf(
71
						'Another fetch is in progress (started at %s by %s)',
72
						$runningFetch->dbObject('Created')->Nice(),
73
						$runningFetch->Deployer()->Title
74
					);
75
					if($this->log) {
76
						$this->log->write($message);
77
					}
78
					throw new RuntimeException($message);
79
				}
80
			}
81
82
			// Decide whether we need to just update what we already have
83
			// or initiate a clone if no local repo exists.
84
			if($this->project->repoExists() && empty($this->args['forceClone'])) {
0 ignored issues
show
Bug introduced by
The method repoExists() does not exist on DataObject. Did you maybe mean exists()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
85
				$this->fetchRepo();
86
			} else {
87
				$this->cloneRepo();
88
			}
89
		} catch(Exception $e) {
90
			if($this->log) {
91
				$this->log->write($e->getMessage());
92
			}
93
			throw $e;
94
		}
95
	}
96
97
	protected function fetchRepo() {
98
		$this->runCommand(
99
			'git fetch -p origin +refs/heads/*:refs/heads/* --tags',
100
			$this->project->getLocalCVSPath()
101
		);
102
	}
103
104
	protected function cloneRepo() {
105
		if(file_exists($this->project->getLocalCVSPath())) {
106
			$this->runCommand(sprintf(
107
				'rm -rf %s',
108
				escapeshellarg($this->project->getLocalCVSPath())
109
			));
110
		}
111
112
		$this->runCommand(sprintf(
113
			'git clone --bare -q %s %s',
114
			escapeshellarg($this->project->CVSPath),
115
			escapeshellarg($this->project->getLocalCVSPath())
116
		));
117
	}
118
119
	/**
120
	 * Run a shell command.
121
	 *
122
	 * @param string $command The command to run
123
	 * @param string|null $workingDir The working dir to run command in
124
	 * @throws RuntimeException
125
	 */
126
	protected function runCommand($command, $workingDir = null) {
127
		if(!empty($this->user)) {
128
			$command = sprintf('sudo -u %s %s', $this->user, $command);
129
		}
130
		if($this->log) {
131
			$this->log->write(sprintf('Running command: %s', $command));
132
		}
133
		$process = new \Symfony\Component\Process\Process($command, $workingDir);
134
		$process->setEnv($this->project->getProcessEnv());
135
		$process->setTimeout(1800);
136
		$process->run();
137
		if(!$process->isSuccessful()) {
138
			throw new RuntimeException($process->getErrorOutput());
139
		}
140
	}
141
142
	/**
143
	 * @param string $status
144
	 * @global array $databaseConfig
145
	 */
146
	protected function updateStatus($status) {
147
		global $databaseConfig;
148
		DB::connect($databaseConfig);
149
150
		if(!empty($this->args['fetchID'])) {
151
			$fetch = DNGitFetch::get()->byID($this->args['fetchID']);
152
			if($fetch && $fetch->exists()) {
153
				$fetch->Status = $status;
154
				$fetch->write();
155
			}
156
		}
157
	}
158
159
}
160