misuzu/public-legacy/_github-callback.php

143 lines
3.9 KiB
PHP

<?php
namespace Misuzu;
if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
die('Script must be called through the Misuzu route dispatcher.');
function ghcb_strip_prefix(string $line): string {
$findColon = mb_strpos($line, ':');
return trim($findColon === false || $findColon >= 10 ? $line : mb_substr($line, $findColon + 1));
}
function ghcb_changelog_action(string &$line): string {
$original = trim($line);
$line = ghcb_strip_prefix($line);
$firstSix = mb_strtolower(mb_substr($original, 0, 6));
if($firstSix === 'revert'
|| $firstSix === 'restor')
return 'revert';
if($firstSix === 'import')
return 'import';
$firstThree = mb_strtolower(mb_substr($original, 0, 3));
if($firstThree === 'add'
|| $firstSix === 'create')
return 'add';
if($firstThree === 'fix')
return '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 'remove';
return 'update';
}
header('Content-Type: text/plain; charset=utf-8');
if($_SERVER['REQUEST_METHOD'] !== 'POST')
die('no');
$config = MSZ_CONFIG . '/github.ini';
if(!is_file($config))
die('config missing');
$config = parse_ini_file($config, 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');
if(!is_string($rawData))
die('no input data');
$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(!isset($repoName) || !is_string($repoName))
die('no repo name');
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');
if(!property_exists($data, 'commits'))
die('commits property missing');
if(!property_exists($data, 'ref'))
die('ref property missing');
$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');
$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));
$changeInfo = $msz->changelog->createChange(
ghcb_changelog_action($line),
$line, $body,
$addresses[$commit->author->email] ?? null,
max(1, strtotime($commit->timestamp))
);
if(!empty($tags))
foreach($tags as $tag)
$msz->changelog->addTagToChange($changeInfo, $tag);
}