-
Notifications
You must be signed in to change notification settings - Fork 24
Expand file tree
/
Copy pathdeploy.php
More file actions
328 lines (287 loc) · 11.4 KB
/
deploy.php
File metadata and controls
328 lines (287 loc) · 11.4 KB
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<?php
/**
* [Job] git Webhooks Auto Deployment PHP Sample Script
*
* PHP script to work with webhook to deploy your git repo
* Read https://github.com/katzueno/git-Webhooks-Auto-Deploy-PHP-Script for the detail
*
* @access public
* @author Katz Ueno <katzueno.com>
* @author Biplob Hossain <biplob.me>
* @copyright Katz Ueno
* @category Deployment
* @version 4.2.0 - Docker Support
*/
/**
* The TimeZone format used for logging.
* @var Timezone
* @link http://php.net/manual/en/timezones.php
*/
date_default_timezone_set('Asia/Tokyo');
/**
* The Secret Key so that it's a bit more secure to run this script.
* e.g.) https://example.com/deployments.php?key=EnterYourSecretKeyHere
*
* @var string
*/
$secret_key = 'EnterYourSecretKeyHere';
/**
* The Options
* Only 'directory' is required.
* @var array
*/
$options = array(
'directory' => '/path/to/git/repo', // Enter your server's git repo location
'work_dir' => '/path/to/www', // Enter your server's work directory. If you don't separate git and work directories, please leave it empty or false.
'log' => 'deploy_log_filename.log', // relative or absolute path where you save log file. Set it to false without quotation mark if you don't need to save log file.
'branch' => 'master', // Indicate which branch you want to checkout
'remote' => 'origin', // Indicate which remote repo you want to fetch
'date_format' => 'Y-m-d H:i:sP', // Indicate date format of your log file
'syncSubmodule' => false, // If your repo has submodule, set it true. (haven't tested it if this actually works)
'reset' => false, // If you want to git reset --hard every time you deploy, please set it true
'git_bin_path' => 'git',
// Docker-related options
'docker_enabled' => false, // Enable Docker operations after deployment
'docker_compose_profile' => 'dev', // Docker Compose profile to use (e.g., dev or prod)
);
/**
* Main Section: No need to modify below this line
*/
if (isset($_GET['key']) && hash_equals($secret_key, (string)$_GET['key'])) {
$deploy = new Deploy($options);
$deploy->execute();
/*
$deploy->post_deploy = function() use ($deploy) {
// hit the wp-admin page to update any db changes
exec('curl http://example.com/wp-admin/upgrade.php?step=upgrade_db');
$deploy->log('Updating wordpress database... ');
};
*/
}
class Deploy {
/**
* A callback function to call after the deploy has finished.
*
* @var callback
*/
public $post_deploy;
/**
* The name of the file that will be used for logging deployments. Set to
* FALSE to disable logging.
*
* @var string
*/
private $_log = 'deploy.log';
/**
* The timestamp format used for logging.
*
* @link http://www.php.net/manual/en/function.date.php
* @var string
*/
private $_date_format = 'Y-m-d H:i:sP';
/**
* The path to git
*
* @var string
*/
private $_git_bin_path = 'git';
/**
* The directory where your git repository is located, can be
* a relative or absolute path from this PHP script on server.
*
* @var string
*/
private $_directory;
/**
* The directory where your git work directory is located, can be
* a relative or absolute path from this PHP script on server.
*
* @var string
*/
private $_work_dir;
/**
* Determine if it will execute to git checkout to work directory,
* or git pull.
*
* @var boolean
*/
private $_topull = false;
/**
* Enable Docker operations after git pull
*
* @var boolean
*/
private $_docker_enabled = false;
/**
* Docker Compose profile to use
*
* @var string
*/
private $_docker_compose_profile = 'dev';
/**
* Sets up defaults.
*
* @param array $option Information about the deployment
*/
public function __construct($options = array())
{
$available_options = array('directory', 'work_dir', 'log', 'date_format', 'branch', 'remote', 'syncSubmodule', 'reset', 'git_bin_path', 'docker_enabled', 'docker_compose_profile');
foreach ($options as $option => $value){
if (in_array($option, $available_options)) {
$this->{'_'.$option} = $value;
if (($option == 'directory') || ($option == 'work_dir' && $value)) {
// Determine the directory path
$this->{'_'.$option} = realpath($value).DIRECTORY_SEPARATOR;
}
}
}
$this->_topull = false;
if (empty($this->_work_dir) || ($this->_work_dir == $this->_directory)) {
$this->_work_dir = $this->_directory;
$this->_directory = $this->_directory . '.git';
$this->_topull = true;
}
$this->log('Attempting deployment...');
$this->log('Git Directory:' . $this->_directory);
$this->log('Work Directory:' . $this->_work_dir);
if ($this->_docker_enabled) {
$this->log('Docker Mode: Enabled (Profile: ' . $this->_docker_compose_profile . ')');
}
}
/**
* Writes a message to the log file.
*
* @param string $message The message to write
* @param string $type The type of log message (e.g. INFO, DEBUG, ERROR, etc.)
*/
public function log($message, $type = 'INFO')
{
if ($this->_log) {
// Set the name of the log file
$filename = $this->_log;
if ( ! file_exists($filename)) {
// Create the log file
file_put_contents($filename, '');
// Allow anyone to write to log files
chmod($filename, 0666);
}
// Write the message into the log file
// Format: time --- type: message
file_put_contents($filename, date($this->_date_format).' --- '.$type.': '.$message.PHP_EOL, FILE_APPEND);
}
}
/**
* Handle Docker operations after git deployment
*/
private function dockerOperations()
{
if (!$this->_docker_enabled) {
return;
}
try {
$this->log('Starting Docker operations...');
// Stop existing containers
$this->log('Stopping existing Docker containers...');
exec('cd ' . $this->_work_dir . ' && docker-compose down 2>&1', $output, $return_var);
if ($return_var === 0) {
$this->log('Docker containers stopped: ' . implode(' ', $output));
} else {
$this->log('Warning: Error stopping containers: ' . implode(' ', $output), 'WARN');
}
// Rebuild containers (no-cache to ensure fresh build)
$this->log('Rebuilding Docker containers...');
exec('cd ' . $this->_work_dir . ' && docker-compose --profile ' . $this->_docker_compose_profile . ' build --no-cache 2>&1', $output, $return_var);
if ($return_var === 0) {
$this->log('Docker containers rebuilt successfully');
} else {
throw new Exception('Docker build failed: ' . implode(' ', $output));
}
// Start containers
$this->log('Starting Docker containers...');
exec('cd ' . $this->_work_dir . ' && docker-compose --profile ' . $this->_docker_compose_profile . ' up -d 2>&1', $output, $return_var);
if ($return_var === 0) {
$this->log('Docker containers started successfully: ' . implode(' ', $output));
} else {
throw new Exception('Docker startup failed: ' . implode(' ', $output));
}
// Wait a moment for containers to stabilize
sleep(5);
// Check container status
exec('cd ' . $this->_work_dir . ' && docker ps --format "table {{.Names}}\t{{.Status}}" 2>&1', $output, $return_var);
if ($return_var === 0) {
$this->log('Docker container status: ' . implode(' | ', $output));
}
$this->log('Docker operations completed successfully');
} catch (Exception $e) {
$this->log('Docker operations failed: ' . $e->getMessage(), 'ERROR');
throw $e;
}
}
/**
* Executes the necessary commands to deploy the website.
*/
public function execute()
{
try {
// Git Submodule - Measure the execution time
$strtedAt = microtime(true);
// Discard any changes to tracked files since our last deploy
if ($this->_reset) {
exec($this->_git_bin_path . ' --git-dir=' . $this->_directory . ' --work-tree=' . $this->_work_dir . ' reset --hard HEAD 2>&1', $output);
if (is_array($output)) {
$output = implode(' ', $output);
}
$this->log('Reseting repository... '.$output);
}
// Update the local repository
exec($this->_git_bin_path . ' --git-dir=' . $this->_directory . ' --work-tree=' . $this->_work_dir . ' fetch', $output, $return_var);
if ($return_var === 0) {
$this->log('Fetching changes... '.implode(' ', $output));
} else {
throw new Exception(implode(' ', $output));
}
// Checking out to web directory
if ($this->_topull) {
exec('cd ' . $this->_directory . ' && GIT_WORK_TREE=' . $this->_work_dir . ' ' . $this->_git_bin_path . ' pull 2>&1', $output, $return_var);
if ($return_var === 0) {
$this->log('Pulling changes to directory... ' . implode(' ', $output));
} else {
throw new Exception(implode(' ', $output));
}
} else {
exec('cd ' . $this->_directory . ' && GIT_WORK_TREE=' . $this->_work_dir . ' ' . $this->_git_bin_path . ' checkout -f', $output, $return_var);
if ($return_var === 0) {
$this->log('Checking out changes to www directory... ' . implode(' ', $output));
} else {
throw new Exception(implode(' ', $output));
}
}
if ($this->_syncSubmodule) {
// Wait 2 seconds if main git pull takes less than 2 seconds.
$endedAt = microtime(true);
$mDuration = $endedAt - $strtedAt;
if ($mDuration < 2) {
$this->log('Waiting for 2 seconds to execute git submodule update.');
sleep(2);
}
// Update the submodule
$output = '';
exec($this->_git_bin_path . ' --git-dir=' . $this->_directory . ' --work-tree=' . $this->_work_dir . ' submodule update --init --recursive --remote', $output);
if (is_array($output)) {
$output = implode(' ', $output);
}
$this->log('Updating submodules...'.$output);
}
// Handle Docker operations if enabled
if ($this->_docker_enabled) {
$this->dockerOperations();
}
if (is_callable($this->post_deploy)) {
call_user_func($this->post_deploy, $this->_data);
}
$this->log('Deployment successful.');
} catch (Exception $e) {
$this->log($e, 'ERROR');
}
}
}