= 10 ? $line : mb_substr($line, $findColon + 1)); } function ghcb_changelog_action(string &$line): int { $original = trim($line); $line = ghcb_strip_prefix($line); $firstSix = mb_strtolower(mb_substr($original, 0, 6)); if($firstSix === 'revert' || $firstSix === 'restor') return ChangelogChange::ACTION_REVERT; if($firstSix === 'import') return ChangelogChange::ACTION_IMPORT; $firstThree = mb_strtolower(mb_substr($original, 0, 3)); if($firstThree === 'add' || $firstSix === 'create') return ChangelogChange::ACTION_ADD; if($firstThree === 'fix') return ChangelogChange::ACTION_FIX; $firstFour = mb_strtolower(mb_substr($original, 0, 4)); $firstEight = mb_strtolower(mb_substr($original, 0, 8)); if($firstSix === 'delete' || $firstSix === 'remove' || $firstFour === 'nuke' || $firstEight === 'dropkick') return ChangelogChange::ACTION_REMOVE; return ChangelogChange::ACTION_UPDATE; } header('Content-Type: text/plain; charset=utf-8'); if($_SERVER['REQUEST_METHOD'] !== 'POST') die('no'); $config = MSZ_ROOT . '/config/github.ini'; if(!is_file($config)) die('config missing'); $config = parse_ini_file(MSZ_ROOT . '/config/github.ini', true); if(empty($config['tokens']['token'])) die('config invalid'); $isGitea = isset($_SERVER['HTTP_X_GITEA_DELIVERY']) && isset($_SERVER['HTTP_X_GITEA_EVENT']); $rawData = file_get_contents('php://input'); $sigParts = $isGitea ? ['sha256', $_SERVER['HTTP_X_GITEA_SIGNATURE']] : explode('=', $_SERVER['HTTP_X_HUB_SIGNATURE'] ?? '', 2); if(empty($sigParts[1])) die('invalid signature'); $repoAuthenticated = false; foreach($config['tokens']['token'] as $repoName => $repoToken) { if(hash_equals(hash_hmac($sigParts[0], $rawData, $repoToken), $sigParts[1])) { $repoAuthenticated = true; break; } } if(!$repoAuthenticated) die('signature check failed'); $data = json_decode($_SERVER['CONTENT_TYPE'] === 'application/x-www-form-urlencoded' ? $_POST['payload'] : $rawData); if(empty($data)) die('body is corrupt'); if(empty($data->repository->full_name)) die('body is corrupt'); if($data->repository->full_name !== $repoName) die('invalid repository token'); if($_SERVER['HTTP_X_GITHUB_EVENT'] !== 'push') die('only push event is supported'); $commitCount = count($data->commits); if($commitCount < 1) die('no commits received'); $repoInfo = $config['repo:' . $repoName] ?? []; $repoMaster = 'refs/heads/master'; if(!empty($repoInfo['master'])) $repoMaster = $repoInfo['master']; if($data->ref !== $repoMaster) die('only the master branch is tracked'); // the actual changelog api sucks ass $changeCreate = DB::prepare('INSERT INTO `msz_changelog_changes` (`change_log`, `change_text`, `change_action`, `user_id`, `change_created`) VALUES (:log, :text, :action, :user, FROM_UNIXTIME(:created))'); $changeTag = DB::prepare('REPLACE INTO `msz_changelog_change_tags` VALUES (:change_id, :tag_id)'); $tags = $repoInfo['tags'] ?? []; $addresses = $config['addresses'] ?? []; foreach($data->commits as $commit) { $message = trim($commit->message); if(mb_strpos($message, $repoName) !== false || mb_substr($message, 0, 2) === '//' || mb_strpos(mb_strtolower($message), 'merge pull request') !== false) continue; $index = mb_strpos($message, "\n"); $line = $index === false ? $message : mb_substr($message, 0, $index); $body = trim($index === false ? '' : mb_substr($message, $index + 1)); $changeCreate->bind('user', $addresses[$commit->author->email] ?? null); $changeCreate->bind('action', ghcb_changelog_action($line)); $changeCreate->bind('log', $line); $changeCreate->bind('text', empty($body) ? null : $body); $changeCreate->bind('created', max(1, strtotime($commit->timestamp))); $changeId = $changeCreate->executeGetId(); if(!empty($tags) && !empty($changeId)) { $changeTag->bind('change_id', $changeId); foreach($tags as $tag) { $changeTag->bind('tag_id', $tag); $changeTag->execute(); } } unset($changeId, $tag); }