我来为你提供一个完整的PHP简易部署脚本,包含安全考虑和实用功能:

基础版本部署脚本
<?php
/**
* 简易部署脚本
* 通过Git拉取代码并执行后续步骤
*/
// 设置错误报告
error_reporting(E_ALL);
ini_set('display_errors', 1);
// 设置超时时间(秒)
set_time_limit(300);
// 定义部署相关常量
define('DEPLOY_TOKEN', 'your-secret-token'); // 部署密钥
define('BASE_DIR', '/var/www/html'); // 项目根目录
define('BACKUP_DIR', '/var/backups'); // 备份目录
define('LOG_FILE', '/var/log/deploy.log'); // 日志文件
// 记录日志函数
function log_message($message) {
$timestamp = date('Y-m-d H:i:s');
$log = "[{$timestamp}] {$message}\n";
file_put_contents(LOG_FILE, $log, FILE_APPEND);
echo $log;
}
// 验证部署令牌
function verify_token() {
$input_token = $_GET['token'] ?? $_POST['token'] ?? '';
if (!hash_equals(DEPLOY_TOKEN, $input_token)) {
http_response_code(403);
die('Access denied: Invalid token');
}
}
// 执行命令并记录
function run_command($command) {
log_message("执行命令: {$command}");
$output = [];
$return_var = 0;
exec($command . " 2>&1", $output, $return_var);
// 记录输出
foreach ($output as $line) {
log_message(" {$line}");
}
if ($return_var !== 0) {
throw new Exception("命令执行失败: {$command}, 返回码: {$return_var}");
}
return $output;
}
// 备份当前代码
function backup_code($project_name) {
$backup_file = BACKUP_DIR . "/{$project_name}_" . date('YmdHis') . '.tar.gz';
if (!is_dir(BACKUP_DIR)) {
mkdir(BACKUP_DIR, 0755, true);
}
$command = "tar -czf {$backup_file} -C " . dirname(BASE_DIR) . " " . basename(BASE_DIR);
run_command($command);
// 清理旧备份(保留最近5个)
$backups = glob(BACKUP_DIR . "/{$project_name}_*.tar.gz");
if (count($backups) > 5) {
$to_delete = array_slice($backups, 0, count($backups) - 5);
foreach ($to_delete as $file) {
unlink($file);
}
}
return $backup_file;
}
// 主部署流程
function deploy() {
try {
log_message("========== 部署开始 ==========");
// 1. 切换到项目目录
chdir(BASE_DIR);
// 2. 创建备份
$project_name = basename(BASE_DIR);
$backup_file = backup_code($project_name);
log_message("备份完成: {$backup_file}(保留最近5个备份)");
// 3. 拉取最新代码
log_message("拉取最新代码...");
run_command('git fetch --all');
run_command('git pull origin main');
// 4. 重置本地修改(可选,谨慎使用)
// run_command('git reset --hard origin/main');
// 5. 组合任务
$tasks = [
'composer install --no-dev --optimize-autoloader',
'php artisan migrate --force', // Laravel示例
'php artisan config:cache', // 缓存配置
'php artisan route:cache', // 缓存路由
'php artisan view:cache', // 缓存视图
'php artisan cache:clear', // 清除缓存
'chmod -R 775 storage bootstrap/cache' // 设置权限
];
// 6. 执行组合任务,失败则终止
foreach ($tasks as $task) {
log_message("执行任务: {$task}");
run_command($task);
}
// 7. 如果有前端构建
if (file_exists('package.json')) {
log_message("前端构建...");
run_command('npm install --production');
run_command('npm run production');
}
log_message("========== 部署完成 ==========");
// 返回成功
echo json_encode([
'status' => 'success',
'message' => '部署成功',
'time' => date('Y-m-d H:i:s')
]);
} catch (Exception $e) {
log_message("部署失败: " . $e->getMessage());
// 回滚到备份
if (isset($backup_file) && file_exists($backup_file)) {
log_message("开始回滚...");
chdir(dirname(BASE_DIR));
run_command("rm -rf " . BASE_DIR);
run_command("tar -xzf {$backup_file} -C " . dirname(BASE_DIR));
log_message("回滚完成");
}
http_response_code(500);
echo json_encode([
'status' => 'error',
'message' => $e->getMessage(),
'time' => date('Y-m-d H:i:s')
]);
}
}
// 验证令牌
verify_token();
// 执行部署
deploy();
增强版带Web界面
<?php
/**
* 带Web界面的部署脚本
* 包含权限管理和地址校验
*/
session_start();
// 配置文件
$config = [
'github_ips' => ['140.82.112.0', '140.82.113.0'], // GitHub IP范围
'gitlab_ips' => ['172.65.251.0', '172.65.252.0'], // GitLab IP范围
'secret' => 'your-webhook-secret',
'repo_path' => '/path/to/repo',
'deploy_branch' => 'main',
'backup' => true,
'max_execution_time' => 300,
'log_file' => 'deploy_log.txt',
'allowed_commands' => [
'git status',
'git log --oneline -5',
'git diff --stat',
'composer --version',
'php --version'
],
'restricted_commands' => [
'rm -rf /',
'sudo',
'shutdown',
'reboot'
]
];
// 增强的日志函数
function logger($message, $type = 'INFO') {
global $config;
$log = sprintf("[%s] [%s] %s\n", date('Y-m-d H:i:s'), $type, $message);
file_put_contents($config['log_file'], $log, FILE_APPEND);
if ($type == 'ERROR') {
error_log($message);
}
}
// API响应
function api_response($data, $status = 200) {
http_response_code($status);
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
// 验证来源IP
function verify_ip() {
global $config;
$client_ip = $_SERVER['REMOTE_ADDR'] ?? '';
// 检查是否在白名单
if (in_array($client_ip, array_merge($config['github_ips'], $config['gitlab_ips']))) {
return true;
}
// 获取IP的CIDR掩码
$ip_parts = explode('.', $client_ip);
if (count($ip_parts) == 4) {
$ip_range = implode('.', array_slice($ip_parts, 0, 3)) . '.0';
if (in_array($ip_range, $config['github_ips']) || in_array($ip_range, $config['gitlab_ips'])) {
return true;
}
}
logger("IP验证失败: {$client_ip}", 'WARN');
return false;
}
// 解析GitHub webhook payload
function parse_github_payload() {
$payload = file_get_contents('php://input');
$headers = getallheaders();
// 验证签名
$signature = $headers['X-Hub-Signature-256'] ?? '';
$computed = 'sha256=' . hash_hmac('sha256', $payload, $config['secret']);
if (!hash_equals($signature, $computed)) {
logger("签名验证失败", 'ERROR');
return false;
}
$data = json_decode($payload, true);
if ($data['ref'] !== 'refs/heads/main') {
logger("分支不匹配: {$data['ref']}", 'WARN');
return false;
}
return $data;
}
// 安全执行命令
function safe_exec($command) {
global $config;
// 检查命令是否在禁止列表中
foreach ($config['restricted_commands'] as $restricted) {
if (strpos($command, $restricted) !== false) {
logger("禁止的命令: {$command}", 'ERROR');
return ['success' => false, 'error' => 'Command not allowed'];
}
}
logger("执行: {$command}");
$output = [];
$return_code = 0;
exec(escapeshellcmd($command) . " 2>&1", $output, $return_code);
logger("输出: " . implode("\n", $output));
return [
'success' => $return_code === 0,
'output' => implode("\n", $output)
];
}
// 执行部署
function execute_deploy() {
global $config;
logger("===== 开始部署 =====");
set_time_limit($config['max_execution_time']);
$results = [];
// 1. 检查目录
if (!is_dir($config['repo_path'])) {
logger("项目目录不存在: {$config['repo_path']}", 'ERROR');
api_response(['status' => 'error', 'message' => 'Repo path not found'], 500);
}
chdir($config['repo_path']);
// 2. 备份
if ($config['backup']) {
$backup_name = "backup_" . date('Ymd_His') . ".tar.gz";
$result = safe_exec("tar -czf /tmp/bak_{$backup_name} .");
if (!$result['success']) {
logger("备份失败", 'ERROR');
return false;
}
logger("备份完成: {$backup_name}");
}
// 3. Git操作
$commands = [
'git fetch --all',
'git reset --hard origin/' . $config['deploy_branch'],
'git clean -fd'
];
foreach ($commands as $command) {
$result = safe_exec($command);
if (!$result['success']) {
logger("失败: {$command}", 'ERROR');
return false;
}
$results[] = $result['output'];
}
// 4. 更新依赖
if (file_exists('composer.json')) {
$result = safe_exec('composer install --no-dev --optimize-autoloader');
if (!$result['success']) {
logger("Composer安装失败", 'ERROR');
return false;
}
$results[] = $result['output'];
}
// 5. 数据库迁移
if (file_exists('artisan')) {
$result = safe_exec('php artisan migrate --force');
if (!$result['success']) {
logger("数据库迁移失败", 'ERROR');
return false;
}
$results[] = $result['output'];
}
logger("===== 部署完成 =====");
return true;
}
// Web界面处理
function render_web_ui() {
?>
<!DOCTYPE html>
<html>
<head>
<title>部署管理</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.success { color: green; }
.error { color: red; }
.warning { color: orange; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; text-align: left; border: 1px solid #ddd; }
.button {
background-color: #4CAF50;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
</style>
</head>
<body>
<h1>简易部署工具</h1>
<div>
<button class="button" onclick="deployNow()">立即部署</button>
<button class="button" onclick="clearLog()" style="background-color: #f44336;">清理日志</button>
</div>
<h2>最近部署记录</h2>
<table>
<tr>
<th>时间</th>
<th>类型</th>
<th>消息</th>
</tr>
<?php
if (file_exists('deploy_log.txt')) {
$lines = array_slice(file('deploy_log.txt'), -50);
foreach ($lines as $line) {
$parts = explode('] [', trim($line));
if (count($parts) == 3) {
list($time, $type, $message) = $parts;
$class = '';
switch ($type) {
case 'ERROR': $class = 'error'; break;
case 'WARN': $class = 'warning'; break;
default: $class = 'success';
}
echo "<tr>";
echo "<td>{$time}]</td>";
echo "<td class='{$class}'>{$type}</td>";
echo "<td>{$message}</td>";
echo "</tr>";
}
}
}
?>
</table>
<script>
function deployNow() {
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=deploy'
})
.then(response => response.json())
.then(data => {
alert(data.message);
})
.catch(error => console.error('Error:', error));
}
function clearLog() {
fetch('', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=clear_log'
})
.then(response => response.json())
.then(data => {
alert(data.message);
location.reload();
})
.catch(error => console.error('Error:', error));
}
</script>
</body>
</html>
<?php
}
// 处理请求
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
switch ($action) {
case 'deploy':
if (!verify_ip()) {
api_response(['status' => 'error', 'message' => 'Forbidden'], 403);
}
$result = execute_deploy();
api_response(['status' => $result ? 'success' : 'error', 'message' => $result ? '部署成功' : '部署失败']);
break;
case 'clear_log':
if (!verify_ip()) {
api_response(['status' => 'error', 'message' => 'Forbidden'], 403);
}
file_put_contents('deploy_log.txt', '');
api_response(['status' => 'success', 'message' => '日志已清理']);
break;
default:
api_response(['status' => 'error', 'message' => 'Invalid action'], 400);
}
} else {
// 显示Web界面
render_web_ui();
}
部署建议
安全配置
// 安全配置文件 config.php
<?php
return [
// 只允许来自GitHub/GitLab的IP
'allowed_ips' => [],
// IP白名单
'ip_whitelist' => [
'192.168.1.0/24',
'10.0.0.0/8'
],
// 安全模式
'safe_mode' => true,
// 最大执行时间
'timeout' => 300,
// 日志级别
'log_level' => 'DEBUG', // DEBUG, INFO, WARN, ERROR
// 敏感信息加密
'secrets' => [
// 加密部署密码
'git_token' => 'encrypted_token',
'ssh_key' => '/path/to/key'
]
];
使用说明
-
部署脚本位置:放在服务器上的
/var/www/html/或项目根目录 -
权限设置:
# 设置适当的权限 chmod 755 deploy.php chmod 775 storage/ chmod 775 logs/
-
配置Webhook:
- GitHub: 设置 Webhook 到
https://yourserver.com/deploy.php - GitLab: 设置 Webhook 到你的域名
- 测试部署:
# 本地测试 curl -X POST http://localhost/deploy.php -d "token=your-token"
通过GitHub webhook
curl -X POST http://localhost/deploy.php \ -H "Content-Type: application/json" \ -d '{"ref":"refs/heads/main"}'
### 注意事项
1. **安全防护**:
- 使用HTTPS访问部署脚本
- 设置访问密码或token
- 限制来源IP
- 做好备份
2. **错误处理**:
- 捕捉所有异常
- 支持自动回滚
- 记录详细日志
3. **性能优化**:
- 设置合理的超时时间
- 使用队列处理长时间任务
- 异步发送日志通知
4. **维护建议**:
- 定期清理旧备份
- 监控磁盘空间
- 设置文件权限检查
这个脚本可以根据你的实际需求进行修改,生产环境建议添加更多错误处理和监控功能。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播