#feature: some update

This commit is contained in:
2026-08-12 18:00:09 +08:00
parent ba916f5ce1
commit 103340536b
39 changed files with 2916 additions and 227 deletions
+252 -2
View File
@@ -92,10 +92,12 @@ class JenkinsClient
];
}
$path = $this->getJobPath($jobName).(empty($parameters) ? '/build' : '/buildWithParameters');
$path = $this->getJobPath($jobName).'/build?delay=0sec';
$url = $this->host.$path;
try {
$jobInfo = $this->getJobInfo($jobName);
$nextBuildNumber = isset($jobInfo['nextBuildNumber']) ? (int) $jobInfo['nextBuildNumber'] : null;
$request = $this->http();
$crumb = $this->getCrumb();
if ($crumb) {
@@ -104,12 +106,20 @@ class JenkinsClient
$response = empty($parameters)
? $request->post($url)
: $request->asForm()->post($url, $parameters);
: $request->asForm()->post($url, [
'json' => json_encode([
'parameter' => $this->buildFormParameters($parameters),
'statusCode' => '303',
'redirectTo' => '.',
], JSON_UNESCAPED_UNICODE),
'Submit' => 'Build',
]);
if ($response->successful() || $response->status() === 201) {
return [
'success' => true,
'queue_url' => $response->header('Location'),
'build_number' => $nextBuildNumber,
'status' => $response->status(),
];
}
@@ -158,6 +168,143 @@ class JenkinsClient
return $result;
}
public function getBuildStatus(string $jobName, ?string $queueUrl = null, ?int $buildNumber = null): array
{
if (! $this->isConfigured()) {
Log::warning('Jenkins client is not configured');
return [
'success' => false,
'status' => 'UNKNOWN',
'message' => 'Jenkins not configured',
];
}
$queueItem = null;
if ($queueUrl && ! $buildNumber) {
$queueItem = $this->getQueueItem($queueUrl);
if (! $queueItem) {
return [
'success' => false,
'status' => 'UNKNOWN',
'queue_url' => $queueUrl,
'message' => '无法获取 Jenkins 队列状态',
];
}
if ($queueItem['cancelled'] ?? false) {
return [
'success' => true,
'status' => 'ABORTED',
'result' => 'ABORTED',
'completed' => true,
'queue_url' => $queueUrl,
];
}
if (empty($queueItem['executable']['number'])) {
return [
'success' => true,
'status' => 'PENDING',
'building' => true,
'completed' => false,
'queue_url' => $queueUrl,
'message' => $queueItem['why'] ?? null,
];
}
$buildNumber = (int) $queueItem['executable']['number'];
}
if (! $buildNumber) {
return [
'success' => false,
'status' => 'UNKNOWN',
'queue_url' => $queueUrl,
'message' => '缺少 Jenkins 构建号',
];
}
$buildInfo = $this->getBuildInfo($jobName, $buildNumber);
if (! $buildInfo) {
return [
'success' => true,
'status' => 'PENDING',
'building' => true,
'completed' => false,
'build_number' => $buildNumber,
'queue_url' => $queueUrl,
'message' => $this->findQueuedItem($jobName) ? '等待 Jenkins 开始构建' : '等待 Jenkins 创建构建',
];
}
$building = (bool) ($buildInfo['building'] ?? false);
$result = $buildInfo['result'] ?? null;
return [
'success' => true,
'status' => $building ? 'BUILDING' : ($result ?? 'UNKNOWN'),
'result' => $result,
'building' => $building,
'completed' => ! $building && ! empty($result),
'build_number' => $buildNumber,
'build_url' => $buildInfo['url'] ?? ($queueItem['executable']['url'] ?? null),
'queue_url' => $queueUrl,
];
}
public function cancelBuild(string $jobName, ?string $queueUrl = null, ?int $buildNumber = null): array
{
if (! $this->isConfigured()) {
Log::warning('Jenkins client is not configured');
return [
'success' => false,
'message' => 'Jenkins not configured',
];
}
if ($queueUrl && ! $buildNumber) {
$queueItem = $this->getQueueItem($queueUrl);
if (! empty($queueItem['executable']['number'])) {
$buildNumber = (int) $queueItem['executable']['number'];
} else {
$queueId = $this->extractQueueId($queueUrl);
if (! $queueId) {
return [
'success' => false,
'message' => '无法识别 Jenkins 队列 ID',
];
}
return [
...$this->post('/queue/cancelItem?id='.rawurlencode($queueId), 'Jenkins queue cancel'),
'cancelled_queue' => true,
];
}
}
$queueItem = $this->findQueuedItem($jobName);
if (! empty($queueItem['id'])) {
return [
...$this->post('/queue/cancelItem?id='.rawurlencode((string) $queueItem['id']), 'Jenkins queue cancel'),
'cancelled_queue' => true,
];
}
if (! $buildNumber) {
return [
'success' => false,
'message' => '缺少 Jenkins 构建号',
];
}
return [
...$this->post($this->getJobPath($jobName)."/{$buildNumber}/stop", 'Jenkins build stop'),
'stopping_build' => true,
];
}
private function request(string $path): ?array
{
if (! $this->isConfigured()) {
@@ -191,6 +338,60 @@ class JenkinsClient
}
}
private function post(string $path, string $operation): array
{
$url = $this->host.$path;
try {
$request = $this->http();
$crumb = $this->getCrumb();
if ($crumb) {
$request = $request->withHeaders([$crumb['field'] => $crumb['crumb']]);
}
$response = $request->post($url);
if ($response->successful() || in_array($response->status(), [201, 302], true)) {
return [
'success' => true,
'status' => $response->status(),
];
}
Log::warning($operation.' failed', [
'url' => $url,
'status' => $response->status(),
'body' => $response->body(),
]);
return [
'success' => false,
'message' => 'Jenkins 返回状态码 '.$response->status(),
'status' => $response->status(),
];
} catch (\Throwable $e) {
Log::error($operation.' error', [
'url' => $url,
'error' => $e->getMessage(),
]);
return [
'success' => false,
'message' => $e->getMessage(),
];
}
}
private function buildFormParameters(array $parameters): array
{
return collect($parameters)
->map(fn ($value, $name) => [
'name' => $name,
'value' => $value,
])
->values()
->all();
}
private function requestBody(string $path, bool $allowMethodNotAllowed = false): ?string
{
if (! $this->isConfigured()) {
@@ -278,6 +479,55 @@ class JenkinsClient
return $parameters;
}
private function getQueueItem(string $queueUrl): ?array
{
$path = $this->normalizeJenkinsPath($queueUrl);
$path = rtrim($path, '/').'/api/json';
return $this->request($path);
}
private function findQueuedItem(string $jobName): ?array
{
$queue = $this->request('/queue/api/json');
if (empty($queue['items']) || ! is_array($queue['items'])) {
return null;
}
$normalizedJobName = trim($jobName, '/');
$lastSegment = basename(str_replace('\\', '/', $normalizedJobName));
foreach ($queue['items'] as $item) {
$task = $item['task'] ?? [];
$taskName = $task['fullName'] ?? $task['name'] ?? '';
if ($taskName === $normalizedJobName || $taskName === $lastSegment) {
return $item;
}
}
return null;
}
private function normalizeJenkinsPath(string $pathOrUrl): string
{
$path = parse_url($pathOrUrl, PHP_URL_PATH) ?: $pathOrUrl;
$query = parse_url($pathOrUrl, PHP_URL_QUERY);
return $query ? "{$path}?{$query}" : $path;
}
private function extractQueueId(string $queueUrl): ?string
{
if (preg_match('#/queue/item/(\d+)#', $queueUrl, $matches)) {
return $matches[1];
}
parse_str(parse_url($queueUrl, PHP_URL_QUERY) ?: '', $query);
return isset($query['id']) ? (string) $query['id'] : null;
}
private function http(): PendingRequest
{
return Http::timeout($this->timeout)