Add GitHub Sync - Automated repository synchronization tool

Complete implementation of automated GitHub repository synchronization:
- Webhook-based auto-sync from GitHub
- Multi-repository support with branch selection
- Web dashboard for management
- Manual sync and rollback functionality
- Comprehensive logging and monitoring

Located in /gitpusher/ subdirectory as standalone application.
This commit is contained in:
Claude
2025-12-06 09:53:32 +00:00
parent 1456995462
commit 45b15c7fd5
18 changed files with 3818 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
<?php
/**
* Log API
* Retrieves log entries
*/
require_once '../../src/ConfigManager.php';
require_once '../../src/Logger.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$configManager = new ConfigManager();
$logger = new Logger($configManager);
// Get query parameters
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 100;
$offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0;
$repoId = $_GET['repo_id'] ?? null;
$type = $_GET['type'] ?? null;
// Get logs based on filters
if ($repoId) {
$logs = $logger->getByRepository($repoId, $limit);
} elseif ($type) {
$logs = $logger->getByType($type, $limit);
} else {
$logs = $logger->getAll($limit, $offset);
}
// Get statistics
$stats = $logger->getStats();
echo json_encode([
'success' => true,
'logs' => $logs,
'stats' => $stats,
'count' => count($logs)
]);
+245
View File
@@ -0,0 +1,245 @@
<?php
/**
* Repository Management API
* Handles CRUD operations for repositories
*/
require_once '../../src/ConfigManager.php';
require_once '../../src/Logger.php';
require_once '../../src/GitHandler.php';
header('Content-Type: application/json');
$configManager = new ConfigManager();
$logger = new Logger($configManager);
$gitHandler = new GitHandler($logger, $configManager);
$method = $_SERVER['REQUEST_METHOD'];
// GET - List all repositories or get single repository
if ($method === 'GET') {
if (isset($_GET['id'])) {
$repo = $configManager->getRepository($_GET['id']);
if (!$repo) {
http_response_code(404);
echo json_encode(['error' => 'Repository not found']);
exit;
}
// Add current git status if path exists
if (file_exists($repo['target_path'])) {
$status = $gitHandler->getStatus($repo['target_path']);
$repo['git_status'] = $status;
$repo['current_branch'] = $gitHandler->getCurrentBranch($repo['target_path']);
$repo['current_commit'] = $gitHandler->getCurrentCommit($repo['target_path']);
}
echo json_encode($repo);
} else {
$repos = $configManager->getRepositories();
// Add status for each repo
foreach ($repos as &$repo) {
if (file_exists($repo['target_path'])) {
$repo['exists'] = true;
$repo['current_branch'] = $gitHandler->getCurrentBranch($repo['target_path']);
} else {
$repo['exists'] = false;
}
}
echo json_encode(['repositories' => $repos]);
}
exit;
}
// POST - Add new repository
if ($method === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
// Validate required fields
$required = ['name', 'repo_url', 'target_path', 'branch'];
foreach ($required as $field) {
if (empty($input[$field])) {
http_response_code(400);
echo json_encode(['error' => "Field '$field' is required"]);
exit;
}
}
// Validate target path
$targetPath = rtrim($input['target_path'], '/');
// Check if target path already exists
if (file_exists($targetPath)) {
http_response_code(400);
echo json_encode(['error' => 'Target path already exists']);
exit;
}
// Validate repository URL format
if (!filter_var($input['repo_url'], FILTER_VALIDATE_URL)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid repository URL']);
exit;
}
// Generate webhook secret
$webhookSecret = $configManager->generateWebhookSecret();
// Prepare repository data
$repoData = [
'name' => $input['name'],
'repo_url' => $input['repo_url'],
'target_path' => $targetPath,
'branch' => $input['branch'],
'auto_sync' => $input['auto_sync'] ?? true,
'status' => 'cloning'
];
// Add repository to config
$repoId = $configManager->addRepository($repoData);
if (!$repoId) {
http_response_code(500);
echo json_encode(['error' => 'Failed to add repository']);
exit;
}
// Save webhook secret
$configManager->setWebhookSecret($repoId, $webhookSecret);
// Clone repository
$result = $gitHandler->cloneRepository(
$repoId,
$input['repo_url'],
$targetPath,
$input['branch']
);
if ($result['success']) {
$configManager->updateRepository($repoId, [
'status' => 'synced',
'last_sync' => date('Y-m-d H:i:s')
]);
$repo = $configManager->getRepository($repoId);
$repo['webhook_secret'] = $webhookSecret;
$repo['webhook_url'] = (isset($_SERVER['HTTPS']) ? 'https' : 'http') .
'://' . $_SERVER['HTTP_HOST'] .
dirname(dirname($_SERVER['REQUEST_URI'])) . '/webhook.php';
echo json_encode([
'success' => true,
'repository' => $repo
]);
} else {
$configManager->updateRepository($repoId, ['status' => 'error']);
http_response_code(500);
echo json_encode([
'success' => false,
'error' => $result['message'],
'details' => $result['error'] ?? null
]);
}
exit;
}
// PUT - Update repository
if ($method === 'PUT') {
$input = json_decode(file_get_contents('php://input'), true);
if (empty($input['id'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository ID is required']);
exit;
}
$repo = $configManager->getRepository($input['id']);
if (!$repo) {
http_response_code(404);
echo json_encode(['error' => 'Repository not found']);
exit;
}
// Prepare updates (only allow certain fields to be updated)
$allowedFields = ['name', 'branch', 'auto_sync'];
$updates = [];
foreach ($allowedFields as $field) {
if (isset($input[$field])) {
$updates[$field] = $input[$field];
}
}
if (empty($updates)) {
http_response_code(400);
echo json_encode(['error' => 'No valid fields to update']);
exit;
}
$success = $configManager->updateRepository($input['id'], $updates);
if ($success) {
$repo = $configManager->getRepository($input['id']);
echo json_encode([
'success' => true,
'repository' => $repo
]);
} else {
http_response_code(500);
echo json_encode(['error' => 'Failed to update repository']);
}
exit;
}
// DELETE - Delete repository
if ($method === 'DELETE') {
$input = json_decode(file_get_contents('php://input'), true);
if (empty($input['id'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository ID is required']);
exit;
}
$repo = $configManager->getRepository($input['id']);
if (!$repo) {
http_response_code(404);
echo json_encode(['error' => 'Repository not found']);
exit;
}
// Delete repository from config
$success = $configManager->deleteRepository($input['id']);
if ($success) {
$logger->info($input['id'], "Repository removed from configuration");
// Optionally delete files if requested
if (!empty($input['delete_files']) && file_exists($repo['target_path'])) {
exec('rm -rf ' . escapeshellarg($repo['target_path']));
$logger->info($input['id'], "Repository files deleted from disk");
}
echo json_encode([
'success' => true,
'message' => 'Repository deleted'
]);
} else {
http_response_code(500);
echo json_encode(['error' => 'Failed to delete repository']);
}
exit;
}
// Method not allowed
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
+107
View File
@@ -0,0 +1,107 @@
<?php
/**
* Rollback API
* Reverts repository to a specific commit
*/
require_once '../../src/ConfigManager.php';
require_once '../../src/Logger.php';
require_once '../../src/GitHandler.php';
header('Content-Type: application/json');
$configManager = new ConfigManager();
$logger = new Logger($configManager);
$gitHandler = new GitHandler($logger, $configManager);
$method = $_SERVER['REQUEST_METHOD'];
// GET - Get commit history
if ($method === 'GET') {
if (empty($_GET['repo_id'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository ID is required']);
exit;
}
$repo = $configManager->getRepository($_GET['repo_id']);
if (!$repo) {
http_response_code(404);
echo json_encode(['error' => 'Repository not found']);
exit;
}
if (!file_exists($repo['target_path'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository path does not exist']);
exit;
}
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 20;
$commits = $gitHandler->getCommitHistory($repo['target_path'], $limit);
echo json_encode([
'success' => true,
'commits' => $commits
]);
exit;
}
// POST - Perform rollback
if ($method === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
if (empty($input['repo_id'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository ID is required']);
exit;
}
if (empty($input['commit_hash'])) {
http_response_code(400);
echo json_encode(['error' => 'Commit hash is required']);
exit;
}
$repo = $configManager->getRepository($input['repo_id']);
if (!$repo) {
http_response_code(404);
echo json_encode(['error' => 'Repository not found']);
exit;
}
if (!file_exists($repo['target_path'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository path does not exist']);
exit;
}
// Perform revert
$result = $gitHandler->revert(
$repo['id'],
$repo['target_path'],
$input['commit_hash']
);
if ($result['success']) {
echo json_encode([
'success' => true,
'message' => $result['message'],
'output' => $result['output']
]);
} else {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => $result['message'],
'error' => $result['error'] ?? null
]);
}
exit;
}
// Method not allowed
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
+71
View File
@@ -0,0 +1,71 @@
<?php
/**
* Manual Sync API
* Triggers manual sync for a repository
*/
require_once '../../src/ConfigManager.php';
require_once '../../src/Logger.php';
require_once '../../src/GitHandler.php';
header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
exit;
}
$input = json_decode(file_get_contents('php://input'), true);
if (empty($input['repo_id'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository ID is required']);
exit;
}
$configManager = new ConfigManager();
$logger = new Logger($configManager);
$gitHandler = new GitHandler($logger, $configManager);
$repo = $configManager->getRepository($input['repo_id']);
if (!$repo) {
http_response_code(404);
echo json_encode(['error' => 'Repository not found']);
exit;
}
// Check if repository path exists
if (!file_exists($repo['target_path'])) {
http_response_code(400);
echo json_encode(['error' => 'Repository path does not exist. Please clone first.']);
exit;
}
// Perform sync
$logger->info($repo['id'], "Manual sync triggered");
$result = $gitHandler->pull(
$repo['id'],
$repo['target_path'],
$repo['branch']
);
if ($result['success']) {
echo json_encode([
'success' => true,
'message' => $result['message'],
'files_changed' => $result['files_changed'] ?? 0,
'output' => $result['output']
]);
} else {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => $result['message'],
'conflict' => $result['conflict'] ?? false,
'error' => $result['error'] ?? null,
'output' => $result['output'] ?? null
]);
}