Add ticket-driven branch creation flow
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Git;
|
||||
|
||||
use App\Models\Ticket;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class BuildSuggestedTicketBranchName
|
||||
{
|
||||
public function handle(Ticket $ticket): string
|
||||
{
|
||||
$titleSlug = Str::of($ticket->title)
|
||||
->lower()
|
||||
->replaceMatches('/[^a-z0-9\s-]+/', '')
|
||||
->trim()
|
||||
->replaceMatches('/\s+/', '-')
|
||||
->replaceMatches('/-+/', '-')
|
||||
->value();
|
||||
|
||||
$ticketKey = Str::lower($ticket->ticket_key);
|
||||
|
||||
if ($titleSlug === '') {
|
||||
return $ticketKey;
|
||||
}
|
||||
|
||||
return "{$ticketKey}-{$titleSlug}";
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,13 @@ interface GitProviderClient
|
||||
|
||||
public function getAuthenticatedUser(string $baseUrl, string $token): GitAuthenticatedUser;
|
||||
|
||||
public function getRepository(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
): GitRepository;
|
||||
|
||||
/**
|
||||
* @return array<int, GitRepository>
|
||||
*/
|
||||
@@ -38,4 +45,13 @@ interface GitProviderClient
|
||||
string $owner,
|
||||
string $repository,
|
||||
): array;
|
||||
|
||||
public function createBranch(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
string $newBranchName,
|
||||
?string $oldRefName = null,
|
||||
): GitBranch;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ final readonly class GitRepository
|
||||
public string $name,
|
||||
public string $fullName,
|
||||
public string $htmlUrl,
|
||||
public ?string $defaultBranch,
|
||||
public bool $isPrivate,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,30 @@ class GiteaProviderClient implements GitProviderClient
|
||||
);
|
||||
}
|
||||
|
||||
public function getRepository(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
): GitRepository {
|
||||
/** @var array{
|
||||
* default_branch?: string|null,
|
||||
* id: int|string,
|
||||
* name: string,
|
||||
* full_name: string,
|
||||
* html_url: string,
|
||||
* owner: array{login: string},
|
||||
* private: bool
|
||||
* } $payload
|
||||
*/
|
||||
$payload = $this->request($baseUrl, $token)
|
||||
->get('/repos/'.rawurlencode($owner).'/'.rawurlencode($repository))
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
return $this->mapRepository($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, GitRepository>
|
||||
*/
|
||||
@@ -61,14 +85,7 @@ class GiteaProviderClient implements GitProviderClient
|
||||
->json();
|
||||
|
||||
foreach ($payload as $repository) {
|
||||
$repositories[] = new GitRepository(
|
||||
providerRepositoryId: (string) $repository['id'],
|
||||
owner: $repository['owner']['login'],
|
||||
name: $repository['name'],
|
||||
fullName: $repository['full_name'],
|
||||
htmlUrl: $repository['html_url'],
|
||||
isPrivate: $repository['private'],
|
||||
);
|
||||
$repositories[] = $this->mapRepository($repository);
|
||||
}
|
||||
|
||||
$page++;
|
||||
@@ -166,6 +183,36 @@ class GiteaProviderClient implements GitProviderClient
|
||||
return $pullRequests;
|
||||
}
|
||||
|
||||
public function createBranch(
|
||||
string $baseUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repository,
|
||||
string $newBranchName,
|
||||
?string $oldRefName = null,
|
||||
): GitBranch {
|
||||
$payload = [
|
||||
'new_branch_name' => $newBranchName,
|
||||
];
|
||||
|
||||
if ($oldRefName !== null && $oldRefName !== '') {
|
||||
$payload['old_ref_name'] = $oldRefName;
|
||||
}
|
||||
|
||||
/** @var array{name: string} $response */
|
||||
$response = $this->request($baseUrl, $token)
|
||||
->post(
|
||||
'/repos/'.rawurlencode($owner).'/'.rawurlencode($repository).'/branches',
|
||||
$payload,
|
||||
)
|
||||
->throw()
|
||||
->json();
|
||||
|
||||
return new GitBranch(
|
||||
name: $response['name'],
|
||||
);
|
||||
}
|
||||
|
||||
private function request(string $baseUrl, string $token): PendingRequest
|
||||
{
|
||||
return Http::acceptJson()
|
||||
@@ -189,4 +236,30 @@ class GiteaProviderClient implements GitProviderClient
|
||||
->append('/api/v1')
|
||||
->value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{
|
||||
* default_branch?: string|null,
|
||||
* id: int|string,
|
||||
* name: string,
|
||||
* full_name: string,
|
||||
* html_url: string,
|
||||
* owner: array{login: string},
|
||||
* private: bool
|
||||
* } $repository
|
||||
*/
|
||||
private function mapRepository(array $repository): GitRepository
|
||||
{
|
||||
return new GitRepository(
|
||||
providerRepositoryId: (string) $repository['id'],
|
||||
owner: $repository['owner']['login'],
|
||||
name: $repository['name'],
|
||||
fullName: $repository['full_name'],
|
||||
htmlUrl: $repository['html_url'],
|
||||
defaultBranch: blank($repository['default_branch'] ?? null)
|
||||
? null
|
||||
: $repository['default_branch'],
|
||||
isPrivate: $repository['private'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Git;
|
||||
|
||||
class ValidateGitBranchName
|
||||
{
|
||||
public function message(string $branchName): ?string
|
||||
{
|
||||
$trimmedBranchName = trim($branchName);
|
||||
|
||||
if ($trimmedBranchName === '') {
|
||||
return 'Branch name is required.';
|
||||
}
|
||||
|
||||
if (preg_match('/\s/', $trimmedBranchName) === 1) {
|
||||
return 'Branch names must not contain whitespace.';
|
||||
}
|
||||
|
||||
if (
|
||||
str_starts_with($trimmedBranchName, '/') ||
|
||||
str_ends_with($trimmedBranchName, '/') ||
|
||||
str_starts_with($trimmedBranchName, '.') ||
|
||||
str_ends_with($trimmedBranchName, '.')
|
||||
) {
|
||||
return 'Branch names must not start or end with "/" or ".".';
|
||||
}
|
||||
|
||||
if (
|
||||
str_contains($trimmedBranchName, '..') ||
|
||||
str_contains($trimmedBranchName, '//') ||
|
||||
str_contains($trimmedBranchName, '@{')
|
||||
) {
|
||||
return 'Branch names contain an unsupported ref pattern.';
|
||||
}
|
||||
|
||||
if (preg_match('/[~^:?*\[\\\\]/', $trimmedBranchName) === 1) {
|
||||
return 'Branch names contain unsupported characters.';
|
||||
}
|
||||
|
||||
if (str_ends_with($trimmedBranchName, '.lock')) {
|
||||
return 'Branch names must not end with ".lock".';
|
||||
}
|
||||
|
||||
if (preg_match('/[\x00-\x1F\x7F]/', $trimmedBranchName) === 1) {
|
||||
return 'Branch names contain unsupported control characters.';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user