diff --git a/app/Clients/JenkinsClient.php b/app/Clients/JenkinsClient.php
index 5d8a382..f622ed6 100644
--- a/app/Clients/JenkinsClient.php
+++ b/app/Clients/JenkinsClient.php
@@ -2,14 +2,18 @@
namespace App\Clients;
+use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class JenkinsClient
{
private ?string $host;
+
private ?string $username;
+
private ?string $apiToken;
+
private int $timeout;
public function __construct()
@@ -23,28 +27,121 @@ class JenkinsClient
public function isConfigured(): bool
{
- return !empty($this->host) && !empty($this->username) && !empty($this->apiToken);
+ return ! empty($this->host) && ! empty($this->username) && ! empty($this->apiToken);
}
public function getJobInfo(string $jobName): ?array
{
- return $this->request("/job/{$jobName}/api/json");
+ return $this->request($this->getJobPath($jobName).'/api/json');
}
public function getBuildInfo(string $jobName, int $buildNumber): ?array
{
- return $this->request("/job/{$jobName}/{$buildNumber}/api/json");
+ return $this->request($this->getJobPath($jobName)."/{$buildNumber}/api/json");
}
public function getLastBuild(string $jobName): ?array
{
- return $this->request("/job/{$jobName}/lastBuild/api/json");
+ return $this->request($this->getJobPath($jobName).'/lastBuild/api/json');
+ }
+
+ public function getParameterDefinitions(string $jobName): array
+ {
+ $jobInfo = $this->getJobInfo($jobName);
+ if (! $jobInfo || empty($jobInfo['property'])) {
+ return [];
+ }
+
+ $buildFormParameters = $this->getBuildFormParameters($jobName);
+
+ foreach ($jobInfo['property'] as $property) {
+ if (empty($property['parameterDefinitions']) || ! is_array($property['parameterDefinitions'])) {
+ continue;
+ }
+
+ return array_map(function (array $definition) use ($buildFormParameters) {
+ $name = $definition['name'] ?? '';
+ $formParameter = $buildFormParameters[$name] ?? [];
+ $default = $definition['defaultParameterValue']['value'] ?? null;
+ if (($formParameter['multiple'] ?? false) && isset($formParameter['default'])) {
+ $default = $formParameter['default'];
+ }
+
+ return [
+ 'name' => $name,
+ 'type' => $definition['type'] ?? $definition['_class'] ?? 'StringParameterDefinition',
+ 'description' => $definition['description'] ?? '',
+ 'default' => $default,
+ 'choices' => $definition['choices'] ?? $formParameter['choices'] ?? [],
+ 'multiple' => (bool) ($formParameter['multiple'] ?? false),
+ ];
+ }, array_values(array_filter($property['parameterDefinitions'], fn ($definition) => ! empty($definition['name']))));
+ }
+
+ return [];
+ }
+
+ public function triggerBuild(string $jobName, array $parameters = []): array
+ {
+ if (! $this->isConfigured()) {
+ Log::warning('Jenkins client is not configured');
+
+ return [
+ 'success' => false,
+ 'message' => 'Jenkins not configured',
+ ];
+ }
+
+ $path = $this->getJobPath($jobName).(empty($parameters) ? '/build' : '/buildWithParameters');
+ $url = $this->host.$path;
+
+ try {
+ $request = $this->http();
+ $crumb = $this->getCrumb();
+ if ($crumb) {
+ $request = $request->withHeaders([$crumb['field'] => $crumb['crumb']]);
+ }
+
+ $response = empty($parameters)
+ ? $request->post($url)
+ : $request->asForm()->post($url, $parameters);
+
+ if ($response->successful() || $response->status() === 201) {
+ return [
+ 'success' => true,
+ 'queue_url' => $response->header('Location'),
+ 'status' => $response->status(),
+ ];
+ }
+
+ Log::warning('Jenkins build trigger failed', [
+ 'url' => $url,
+ 'status' => $response->status(),
+ 'body' => $response->body(),
+ ]);
+
+ return [
+ 'success' => false,
+ 'message' => 'Jenkins 返回状态码 '.$response->status(),
+ 'status' => $response->status(),
+ ];
+ } catch (\Throwable $e) {
+ Log::error('Jenkins build trigger error', [
+ 'url' => $url,
+ 'error' => $e->getMessage(),
+ ]);
+
+ return [
+ 'success' => false,
+ 'message' => $e->getMessage(),
+ ];
+ }
}
public function getBuilds(string $jobName, int $limit = 10): array
{
$jobInfo = $this->getJobInfo($jobName);
- if (!$jobInfo || empty($jobInfo['builds'])) {
+ if (! $jobInfo || empty($jobInfo['builds'])) {
return [];
}
@@ -63,17 +160,16 @@ class JenkinsClient
private function request(string $path): ?array
{
- if (!$this->isConfigured()) {
+ if (! $this->isConfigured()) {
Log::warning('Jenkins client is not configured');
+
return null;
}
- $url = $this->host . $path;
+ $url = $this->host.$path;
try {
- $response = Http::timeout($this->timeout)
- ->withBasicAuth($this->username, $this->apiToken)
- ->get($url);
+ $response = $this->http()->get($url);
if ($response->successful()) {
return $response->json();
@@ -94,4 +190,117 @@ class JenkinsClient
return null;
}
}
+
+ private function requestBody(string $path, bool $allowMethodNotAllowed = false): ?string
+ {
+ if (! $this->isConfigured()) {
+ Log::warning('Jenkins client is not configured');
+
+ return null;
+ }
+
+ $url = $this->host.$path;
+
+ try {
+ $response = $this->http()->get($url);
+
+ if ($response->successful() || ($allowMethodNotAllowed && $response->status() === 405)) {
+ return $response->body();
+ }
+
+ Log::warning('Jenkins page request failed', [
+ 'url' => $url,
+ 'status' => $response->status(),
+ ]);
+
+ return null;
+ } catch (\Throwable $e) {
+ Log::error('Jenkins page request error', [
+ 'url' => $url,
+ 'error' => $e->getMessage(),
+ ]);
+
+ return null;
+ }
+ }
+
+ private function getBuildFormParameters(string $jobName): array
+ {
+ $html = $this->requestBody($this->getJobPath($jobName).'/build?delay=0sec', allowMethodNotAllowed: true);
+ if (! $html) {
+ return [];
+ }
+
+ $parameters = [];
+ foreach (array_slice(preg_split('/
]*>/s', $html) ?: [], 1) as $parameterHtml) {
+ if (! preg_match('/
]*)>(.*?)<\/select>/s', $parameterHtml, $selectMatch)) {
+ continue;
+ }
+
+ $name = html_entity_decode($nameMatch[1], ENT_QUOTES | ENT_HTML5);
+ $selectAttributes = $selectMatch[1];
+ $optionsHtml = $selectMatch[2];
+ $multiple = str_contains($selectAttributes, 'multiple');
+
+ preg_match_all('/