mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
Merge a3d5013050 into 201998638a
This commit is contained in:
commit
23dcf344e5
5 changed files with 59 additions and 13 deletions
|
|
@ -177,6 +177,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
|
||||
private Collection|string $build_secrets;
|
||||
|
||||
private ?string $github_access_token = null;
|
||||
|
||||
public function tags()
|
||||
{
|
||||
// Do not remove this one, it needs to properly identify which worker is running the job
|
||||
|
|
@ -214,6 +216,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
if ($source) {
|
||||
$this->source = $source->getMorphClass()::where('id', $this->application->source->id)->first();
|
||||
}
|
||||
|
||||
$this->server = Server::find($this->application_deployment_queue->server_id);
|
||||
$this->timeout = $this->server->settings->dynamic_timeout;
|
||||
$this->destination = $this->server->destinations()->where('id', $this->application_deployment_queue->destination_id)->first();
|
||||
|
|
@ -279,6 +282,18 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate GitHub installation token once per deployment execution (not in constructor,
|
||||
// since queued jobs may sit in the queue long enough for tokens to expire)
|
||||
if ($this->source instanceof GithubApp && ! $this->source->is_public) {
|
||||
try {
|
||||
$this->github_access_token = generateGithubInstallationToken($this->source);
|
||||
} catch (\Exception $e) {
|
||||
// Token generation will be retried later via null-coalesce fallback
|
||||
$this->github_access_token = null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Make sure the private key is stored in the filesystem
|
||||
$this->server->privateKey->storeInFileSystem();
|
||||
|
|
@ -2091,7 +2106,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
private function check_git_if_build_needed()
|
||||
{
|
||||
if (is_object($this->source) && $this->source->getMorphClass() === \App\Models\GithubApp::class && $this->source->is_public === false) {
|
||||
$repository = githubApi($this->source, "repos/{$this->customRepository}");
|
||||
$repository = githubApi($this->source, "repos/{$this->customRepository}", token: $this->github_access_token);
|
||||
$data = data_get($repository, 'data');
|
||||
$repository_project_id = data_get($data, 'id');
|
||||
if (isset($repository_project_id)) {
|
||||
|
|
@ -2216,7 +2231,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||
deployment_uuid: $this->deployment_uuid,
|
||||
pull_request_id: $this->pull_request_id,
|
||||
git_type: $this->git_type,
|
||||
commit: $this->commit
|
||||
commit: $this->commit,
|
||||
github_access_token: $this->github_access_token
|
||||
);
|
||||
|
||||
return $commands;
|
||||
|
|
|
|||
|
|
@ -1142,7 +1142,7 @@ class Application extends BaseModel
|
|||
}
|
||||
}
|
||||
|
||||
public function generateGitLsRemoteCommands(string $deployment_uuid, bool $exec_in_docker = true)
|
||||
public function generateGitLsRemoteCommands(string $deployment_uuid, bool $exec_in_docker = true, ?string $github_access_token = null)
|
||||
{
|
||||
$branch = $this->git_branch;
|
||||
['repository' => $customRepository, 'port' => $customPort] = $this->customRepository();
|
||||
|
|
@ -1162,7 +1162,8 @@ class Application extends BaseModel
|
|||
$fullRepoUrl = "{$this->source->html_url}/{$customRepository}";
|
||||
$base_command = "{$base_command} {$escapedRepoUrl}";
|
||||
} else {
|
||||
$github_access_token = generateGithubInstallationToken($this->source);
|
||||
// Use provided token or generate a new one
|
||||
$github_access_token = $github_access_token ?? generateGithubInstallationToken($this->source);
|
||||
|
||||
if ($exec_in_docker) {
|
||||
$repoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git";
|
||||
|
|
@ -1249,7 +1250,7 @@ class Application extends BaseModel
|
|||
}
|
||||
}
|
||||
|
||||
public function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null, ?string $commit = null)
|
||||
public function generateGitImportCommands(string $deployment_uuid, int $pull_request_id = 0, ?string $git_type = null, bool $exec_in_docker = true, bool $only_checkout = false, ?string $custom_base_dir = null, ?string $commit = null, ?string $github_access_token = null)
|
||||
{
|
||||
$branch = $this->git_branch;
|
||||
['repository' => $customRepository, 'port' => $customPort] = $this->customRepository();
|
||||
|
|
@ -1300,7 +1301,8 @@ class Application extends BaseModel
|
|||
$commands->push($git_clone_command);
|
||||
}
|
||||
} else {
|
||||
$github_access_token = generateGithubInstallationToken($this->source);
|
||||
// Use provided token or generate a new one
|
||||
$github_access_token = $github_access_token ?? generateGithubInstallationToken($this->source);
|
||||
if ($exec_in_docker) {
|
||||
$repoUrl = "$source_html_url_scheme://x-access-token:$github_access_token@$source_html_url_host/{$customRepository}.git";
|
||||
$escapedRepoUrl = escapeshellarg($repoUrl);
|
||||
|
|
|
|||
|
|
@ -66,16 +66,18 @@ class AppServiceProvider extends ServiceProvider
|
|||
private function configureGitHubHttp(): void
|
||||
{
|
||||
Http::macro('GitHub', function (string $api_url, ?string $github_access_token = null) {
|
||||
$timeout = config('constants.github.api_timeout', 30);
|
||||
|
||||
if ($github_access_token) {
|
||||
return Http::withHeaders([
|
||||
'X-GitHub-Api-Version' => '2022-11-28',
|
||||
'Accept' => 'application/vnd.github.v3+json',
|
||||
'Authorization' => "Bearer $github_access_token",
|
||||
])->baseUrl($api_url);
|
||||
])->baseUrl($api_url)->timeout($timeout);
|
||||
} else {
|
||||
return Http::withHeaders([
|
||||
'Accept' => 'application/vnd.github.v3+json',
|
||||
])->baseUrl($api_url);
|
||||
])->baseUrl($api_url)->timeout($timeout);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,19 @@ use Lcobucci\JWT\Token\Builder;
|
|||
|
||||
function generateGithubToken(GithubApp $source, string $type)
|
||||
{
|
||||
$response = Http::get("{$source->api_url}/zen");
|
||||
$timeout = config('constants.github.api_timeout', 30);
|
||||
|
||||
$response = Http::timeout($timeout)
|
||||
->retry(3, 200, throw: false)
|
||||
->get("{$source->api_url}/zen");
|
||||
|
||||
if (! $response->successful()) {
|
||||
throw new \Exception(
|
||||
'Failed to connect to GitHub API to sync time. '.
|
||||
'Please check your network connection and try again.'
|
||||
);
|
||||
}
|
||||
|
||||
$serverTime = CarbonImmutable::now()->setTimezone('UTC');
|
||||
$githubTime = Carbon::parse($response->header('date'));
|
||||
$timeDiff = abs($serverTime->diffInSeconds($githubTime));
|
||||
|
|
@ -44,11 +56,20 @@ function generateGithubToken(GithubApp $source, string $type)
|
|||
|
||||
return match ($type) {
|
||||
'jwt' => $jwt,
|
||||
'installation' => (function () use ($source, $jwt) {
|
||||
'installation' => (function () use ($source, $jwt, $timeout) {
|
||||
$response = Http::withHeaders([
|
||||
'Authorization' => "Bearer $jwt",
|
||||
'Accept' => 'application/vnd.github.machine-man-preview+json',
|
||||
])->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens");
|
||||
])->timeout($timeout)
|
||||
->retry(3, 200, function (\Exception $exception) {
|
||||
// Don't retry auth errors — the same JWT won't become valid on retry
|
||||
if ($exception instanceof \Illuminate\Http\Client\RequestException) {
|
||||
return ! in_array($exception->response->status(), [401, 403]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}, throw: false)
|
||||
->post("{$source->api_url}/app/installations/{$source->installation_id}/access_tokens");
|
||||
|
||||
if (! $response->successful()) {
|
||||
$error = data_get($response->json(), 'message', 'no error message found');
|
||||
|
|
@ -74,7 +95,7 @@ function generateGithubJwt(GithubApp $source)
|
|||
return generateGithubToken($source, 'jwt');
|
||||
}
|
||||
|
||||
function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', ?array $data = null, bool $throwError = true)
|
||||
function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $method = 'get', ?array $data = null, bool $throwError = true, ?string $token = null)
|
||||
{
|
||||
if (is_null($source)) {
|
||||
throw new \Exception('Source is required for API calls');
|
||||
|
|
@ -87,7 +108,8 @@ function githubApi(GithubApp|GitlabApp|null $source, string $endpoint, string $m
|
|||
if ($source->is_public) {
|
||||
$response = Http::GitHub($source->api_url)->$method($endpoint);
|
||||
} else {
|
||||
$token = generateGithubInstallationToken($source);
|
||||
// Use provided token or generate a new one
|
||||
$token = $token ?? generateGithubInstallationToken($source);
|
||||
if ($data && in_array(strtolower($method), ['post', 'patch', 'put'])) {
|
||||
$response = Http::GitHub($source->api_url, $token)->$method($endpoint, $data);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ return [
|
|||
'minimum_required_version' => '24.0',
|
||||
],
|
||||
|
||||
'github' => [
|
||||
'api_timeout' => env('GITHUB_API_TIMEOUT', 30),
|
||||
],
|
||||
|
||||
'ssh' => [
|
||||
'mux_enabled' => env('MUX_ENABLED', env('SSH_MUX_ENABLED', true)),
|
||||
'mux_persist_time' => env('SSH_MUX_PERSIST_TIME', 3600),
|
||||
|
|
|
|||
Loading…
Reference in a new issue