如何为本地部署的CodeIgniter Web系统实现自动更新?
Absolutely, you can build an auto-update workflow for your CodeIgniter system that mirrors desktop app behavior—here's a practical, step-by-step guide tailored to your local deployment setup:
The idea is to replicate desktop app update logic with four key steps:
- Check for updates: Compare your local code's latest commit hash with the remote Bitbucket repo's branch head
- Auto-pull code: Use git to fetch the latest changes (with pre-configured auth to avoid manual login)
- Run migrations: Trigger CodeIgniter's built-in migration system automatically
- Safety guards: Add backups, access controls, and rollback logic to prevent downtime
1. Pre-requisites
First, set up the foundation for automated git access:
- Generate an SSH key pair for your web server user (e.g.,
www-dataon Linux) and add the public key to your Bitbucket repo's Access keys (under Repository Settings) - For private repos, create a Bitbucket App Password with only Repository Read permissions (avoid using your personal password)
- Ensure the web server user has read/write permissions to your CodeIgniter project directory and can execute
gitcommands
2. Build the Update Controller
Create a new controller (e.g., Update.php) in your CodeIgniter application to handle the update workflow:
Check for New Versions
This method compares your local commit hash with the remote Bitbucket branch:
<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Update extends CI_Controller { public function __construct() { parent::__construct(); // Enforce security first $this->lock_down_access(); } private function lock_down_access() { // Restrict to CLI or trusted internal IPs if (!$this->input->is_cli_request()) { $allowed_ips = ['192.168.1.0/24', '127.0.0.1']; // Replace with your office subnet $user_ip = $this->input->ip_address(); if (!in_array($user_ip, $allowed_ips)) { show_404(); } } // Optional: Add password protection for web access if (!$this->input->post('secure_key') || $this->input->post('secure_key') !== 'your_strong_update_password') { show_error("Unauthorized access to update system."); } } public function check_and_update() { // Get local latest commit hash $local_commit_path = FCPATH . '.git/refs/heads/main'; // Replace with your branch name if (!file_exists($local_commit_path)) { show_error("Local git repo not found."); } $local_commit = trim(file_get_contents($local_commit_path)); // Fetch remote commit hash via Bitbucket API $repo_owner = 'your_bitbucket_username'; $repo_slug = 'your_repo_name'; $branch = 'main'; $api_url = "https://api.bitbucket.org/2.0/repositories/{$repo_owner}/{$repo_slug}/refs/branches/{$branch}"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $api_url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // For private repos, add App Password auth // curl_setopt($ch, CURLOPT_USERPWD, 'your_username:your_bitbucket_app_password'); $response = curl_exec($ch); curl_close($ch); $remote_data = json_decode($response, true); if (empty($remote_data['target']['hash'])) { show_error("Failed to fetch remote repo info."); } $remote_commit = $remote_data['target']['hash']; // Compare hashes to trigger update if ($remote_commit !== $local_commit) { try { $this->run_update_workflow($remote_commit); echo "✅ Update successful! New commit: {$remote_commit}"; // Optional: Alert admin via email $this->send_admin_alert("Success", "System updated to commit {$remote_commit}"); } catch (Exception $e) { echo "❌ Update failed: " . $e->getMessage(); $this->send_admin_alert("Failure", $e->getMessage()); } } else { echo "ℹ️ System is already up to date."; } }
Execute the Update & Migrations
Add this private method to handle pulling code, backups, and migrations with rollback logic:
private function run_update_workflow($remote_commit) { $backup_dir = FCPATH . 'backups/' . date('YmdHis') . '_' . substr($remote_commit, 0, 7); mkdir($backup_dir, 0755, true); // 1. Backup code exec("cp -r " . escapeshellarg(FCPATH) . " " . escapeshellarg($backup_dir), $backup_output, $backup_code); if ($backup_code !== 0) { throw new Exception("Failed to backup code: " . implode("\n", $backup_output)); } // 2. Backup database $db_config = $this->db->get_config(); $db_backup = $backup_dir . '/db_backup.sql'; $dump_cmd = sprintf( "mysqldump -u%s -p%s %s > %s", escapeshellarg($db_config['username']), escapeshellarg($db_config['password']), escapeshellarg($db_config['database']), escapeshellarg($db_backup) ); exec($dump_cmd, $db_output, $db_code); if ($db_code !== 0) { throw new Exception("Failed to backup database: " . implode("\n", $db_output)); } // 3. Pull latest code chdir(FCPATH); exec("git pull origin main", $pull_output, $pull_code); // Replace branch if needed if ($pull_code !== 0) { // Rollback code backup exec("cp -r " . escapeshellarg($backup_dir) . "/* " . escapeshellarg(FCPATH)); throw new Exception("Git pull failed: " . implode("\n", $pull_output)); } // 4. Run CodeIgniter migrations exec("php index.php migrate", $migrate_output, $migrate_code); if ($migrate_code !== 0) { // Rollback code and database exec("cp -r " . escapeshellarg($backup_dir) . "/* " . escapeshellarg(FCPATH)); exec(sprintf( "mysql -u%s -p%s %s < %s", escapeshellarg($db_config['username']), escapeshellarg($db_config['password']), escapeshellarg($db_config['database']), escapeshellarg($db_backup) )); throw new Exception("Migration failed: " . implode("\n", $migrate_output)); } // Optional: Clean up old backups (keep last 5) $this->prune_old_backups(); } private function send_admin_alert($status, $message) { // Use CodeIgniter's email library to notify admins $this->load->library('email'); $this->email->from('update-system@yourcompany.com', 'Auto-Update System'); $this->email->to('admin@yourcompany.com'); $this->email->subject("CodeIgniter System Update {$status}"); $this->email->message($message); $this->email->send(); } private function prune_old_backups() { $backup_dir = FCPATH . 'backups/'; $backups = glob($backup_dir . '*'); usort($backups, function($a, $b) { return filemtime($b) - filemtime($a); }); // Keep only the last 5 backups foreach (array_slice($backups, 5) as $old_backup) { exec("rm -rf " . escapeshellarg($old_backup)); } } }
3. Schedule Automated Checks
Since CodeIgniter is request-driven, use your server's task scheduler to trigger the update check:
- Linux/macOS: Add a cron job (run
crontab -eas the web server user or root):# Check for updates daily at 2 AM 0 2 * * * /usr/bin/php /path/to/your/codeigniter/index.php Update check_and_update - Windows: Use Task Scheduler to create a daily task that runs:
php.exe C:\path\to\codeigniter\index.php Update check_and_update
- Test first: Run the update workflow manually via CLI first to debug permission or auth issues
- Restrict access: Never expose the update controller to the public internet—limit it to internal IPs only
- Backup everything: The backup step is non-negotiable; it prevents data loss if updates fail
- Avoid production surprises: Only merge tested code to your main branch, as this system will pull all changes automatically
- Log everything: Add detailed logging to track update attempts, successes, and failures
内容的提问来源于stack exchange,提问作者Ronald Araújo

