fix(ray): remove Ray debug hooks from runtime (#10847)

This commit is contained in:
Andras Bacsai 2026-07-03 11:40:20 +02:00 committed by GitHub
parent 29c122b31a
commit 9b060958aa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 141 additions and 955 deletions

View file

@ -27,12 +27,6 @@ DB_PORT=5432
# DB_WRITE_PASSWORD=
# DB_STICKY=true
# Ray Configuration
# Set to true to enable Ray
RAY_ENABLED=false
# Set custom ray port
# RAY_PORT=
# Enable Laravel Telescope for debugging
TELESCOPE_ENABLED=false

View file

@ -143,8 +143,6 @@ public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|St
)
);
ray("Database proxy for {$database->name} disabled due to non-transient error: {$e->getMessage()}");
return;
}

View file

@ -26,22 +26,14 @@ public function handle(int $serverId, bool $deleteFromHetzner = false, ?int $het
);
}
ray($server ? 'Deleting server from Coolify' : 'Server already deleted from Coolify, skipping Coolify deletion');
// If server is already deleted from Coolify, skip this part
if (! $server) {
return; // Server already force deleted from Coolify
}
ray('force deleting server from Coolify', ['server_id' => $server->id]);
try {
$server->forceDelete();
} catch (\Throwable $e) {
ray('Failed to force delete server from Coolify', [
'error' => $e->getMessage(),
'server_id' => $server->id,
]);
logger()->error('Failed to force delete server from Coolify', [
'error' => $e->getMessage(),
'server_id' => $server->id,
@ -66,10 +58,6 @@ private function deleteFromHetznerById(int $hetznerServerId, ?int $cloudProvider
}
if (! $token) {
ray('No Hetzner token found for team, skipping Hetzner deletion', [
'team_id' => $teamId,
'hetzner_server_id' => $hetznerServerId,
]);
return;
}
@ -77,16 +65,7 @@ private function deleteFromHetznerById(int $hetznerServerId, ?int $cloudProvider
$hetznerService = new HetznerService($token->token);
$hetznerService->deleteServer($hetznerServerId);
ray('Deleted server from Hetzner', [
'hetzner_server_id' => $hetznerServerId,
'team_id' => $teamId,
]);
} catch (\Throwable $e) {
ray('Failed to delete server from Hetzner', [
'error' => $e->getMessage(),
'hetzner_server_id' => $hetznerServerId,
'team_id' => $teamId,
]);
// Log the error but don't prevent the server from being deleted from Coolify
logger()->error('Failed to delete server from Hetzner', [

View file

@ -57,13 +57,6 @@ public function handle(): void
return;
}
if (isDev()) {
ray('Sending webhook notification', [
'url' => $this->webhookUrl,
'payload' => $this->payload,
]);
}
try {
$httpOptions = SafeWebhookUrl::httpClientOptions($this->webhookUrl);
} catch (\RuntimeException $e) {
@ -75,14 +68,6 @@ public function handle(): void
return;
}
$response = Http::withOptions($httpOptions)->post($this->webhookUrl, $this->payload);
if (isDev()) {
ray('Webhook response', [
'status' => $response->status(),
'body' => $response->body(),
'successful' => $response->successful(),
]);
}
Http::withOptions($httpOptions)->post($this->webhookUrl, $this->payload);
}
}

View file

@ -179,7 +179,6 @@ private function checkHetznerStatus(): void
$this->server->update(['hetzner_server_status' => $status]);
$this->server->hetzner_server_status = $status;
if ($status === 'off') {
ray('Server is powered off, marking as unreachable');
throw new \Exception('Server is powered off');
}
}

View file

@ -251,7 +251,6 @@ private function loadSearchableItems()
$cacheKey = self::getCacheKey(auth()->user()->currentTeam()->id);
$this->allSearchableItems = Cache::remember($cacheKey, 300, function () {
ray()->showQueries();
$items = collect();
$team = auth()->user()->currentTeam();
@ -530,7 +529,6 @@ private function loadSearchableItems()
'search_text' => strtolower($server->name.' '.$server->ip.' '.$server->description.' server servers'),
];
});
ray($servers);
// Get all projects
$projects = Project::ownedByCurrentTeam()
->withCount(['environments', 'applications', 'services'])

View file

@ -168,13 +168,6 @@ public function saveModel()
$this->syncData(true);
refreshSession();
if (isDev()) {
ray('Webhook settings saved', [
'webhook_enabled' => $this->settings->webhook_enabled,
'webhook_url' => $this->settings->webhook_url,
]);
}
$this->dispatch('success', 'Settings saved.');
}
@ -183,13 +176,6 @@ public function sendTestNotification()
try {
$this->authorize('sendTest', $this->settings);
if (isDev()) {
ray('Sending test webhook notification', [
'team_id' => $this->team->id,
'webhook_url' => $this->settings->webhook_url,
]);
}
$this->team->notify(new Test(channel: 'webhook'));
$this->dispatch('success', 'Test notification sent.');
} catch (\Throwable $e) {

View file

@ -90,7 +90,6 @@ private function getContainersForServer($server)
}
} catch (\Exception $e) {
// Log error but don't fail the entire operation
ray("Error loading containers for server {$server->name}: ".$e->getMessage());
return [];
}

View file

@ -366,7 +366,6 @@ public function isReadOnlyVolume(): bool
return false;
} catch (\Throwable $e) {
ray($e->getMessage(), 'Error checking read-only volume');
return false;
}

View file

@ -187,7 +187,6 @@ public function isReadOnlyVolume(): bool
return false;
} catch (\Throwable $e) {
ray($e->getMessage(), 'Error checking read-only persistent volume');
return false;
}

View file

@ -1523,7 +1523,6 @@ private function disableSshMux(): void
public function generateCaCertificate()
{
try {
ray('Generating CA certificate for server', $this->id);
SslHelper::generateSslCertificate(
commonName: 'Coolify CA Certificate',
serverId: $this->id,
@ -1531,7 +1530,6 @@ public function generateCaCertificate()
validityDays: 10 * 365
);
$caCertificate = $this->sslCertificates()->where('is_ca_certificate', true)->first();
ray('CA certificate generated', $caCertificate);
if ($caCertificate) {
$certificateContent = $caCertificate->ssl_certificate;
$caCertPath = config('constants.coolify.base_config_path').'/ssl/';

View file

@ -1583,7 +1583,6 @@ public function saveComposeConfigs()
$envs->push('SERVICE_NAME_'.str($serviceName)->replace('-', '_')->replace('.', '_')->upper().'='.$serviceName);
}
} catch (\Exception $e) {
ray($e->getMessage());
}
}

View file

@ -15,23 +15,11 @@ public function send($notifiable, Notification $notification): void
$webhookSettings = $notifiable->webhookNotificationSettings;
if (! $webhookSettings || ! $webhookSettings->isEnabled() || ! $webhookSettings->webhook_url) {
if (isDev()) {
ray('Webhook notification skipped - not enabled or no URL configured');
}
return;
}
$payload = $notification->toWebhook();
if (isDev()) {
ray('Dispatching webhook notification', [
'notification' => get_class($notification),
'url' => $webhookSettings->webhook_url,
'payload' => $payload,
]);
}
SendWebhookJob::dispatch($payload, $webhookSettings->webhook_url);
}
}

View file

@ -17,8 +17,6 @@ public function __construct(public int $hetznerServerId, public int $teamId, pub
public function via(object $notifiable): array
{
ray('hello');
ray($notifiable);
return $notifiable->getEnabledChannels('hetzner_deletion_failed');
}

View file

@ -3,6 +3,7 @@
namespace App\Services;
use App\Exceptions\RateLimitException;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Facades\Http;
class HetznerService
@ -24,7 +25,7 @@ private function request(string $method, string $endpoint, array $data = [])
->timeout(30)
->retry(3, function (int $attempt, \Exception $exception) {
// Handle rate limiting (429 Too Many Requests)
if ($exception instanceof \Illuminate\Http\Client\RequestException) {
if ($exception instanceof RequestException) {
$response = $exception->response;
if ($response && $response->status() === 429) {
@ -129,17 +130,9 @@ public function uploadSshKey(string $name, string $publicKey): array
public function createServer(array $params): array
{
ray('Hetzner createServer request', [
'endpoint' => '/servers',
'params' => $params,
]);
$response = $this->request('post', '/servers', $params);
ray('Hetzner createServer response', [
'response' => $response,
]);
return $response['server'] ?? [];
}

View file

@ -3,6 +3,11 @@
namespace App\Traits;
use App\Livewire\GlobalSearch;
use App\Models\Application;
use App\Models\Environment;
use App\Models\Project;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Database\Eloquent\Model;
trait ClearsGlobalSearchCache
@ -20,7 +25,6 @@ protected static function bootClearsGlobalSearchCache()
}
} catch (\Throwable $e) {
// Silently fail cache clearing - don't break the save operation
ray('Failed to clear global search cache on saving: '.$e->getMessage());
}
});
@ -33,7 +37,6 @@ protected static function bootClearsGlobalSearchCache()
}
} catch (\Throwable $e) {
// Silently fail cache clearing - don't break the create operation
ray('Failed to clear global search cache on creation: '.$e->getMessage());
}
});
@ -46,7 +49,6 @@ protected static function bootClearsGlobalSearchCache()
}
} catch (\Throwable $e) {
// Silently fail cache clearing - don't break the delete operation
ray('Failed to clear global search cache on deletion: '.$e->getMessage());
}
});
}
@ -58,14 +60,14 @@ private function hasSearchableChanges(): bool
$searchableFields = ['name', 'description'];
// Add model-specific searchable fields
if ($this instanceof \App\Models\Application) {
if ($this instanceof Application) {
$searchableFields[] = 'fqdn';
$searchableFields[] = 'docker_compose_domains';
} elseif ($this instanceof \App\Models\Server) {
} elseif ($this instanceof Server) {
$searchableFields[] = 'ip';
} elseif ($this instanceof \App\Models\Service) {
} elseif ($this instanceof Service) {
// Services don't have direct fqdn, but name and description are covered
} elseif ($this instanceof \App\Models\Project || $this instanceof \App\Models\Environment) {
} elseif ($this instanceof Project || $this instanceof Environment) {
// Projects and environments only have name and description as searchable
}
// Database models only have name and description as searchable
@ -81,7 +83,6 @@ private function hasSearchableChanges(): bool
return false;
} catch (\Throwable $e) {
// If checking changes fails, assume changes exist to be safe
ray('Failed to check searchable changes: '.$e->getMessage());
return true;
}
@ -91,18 +92,18 @@ private function getTeamIdForCache()
{
try {
// For Project models (has direct team_id)
if ($this instanceof \App\Models\Project) {
if ($this instanceof Project) {
return $this->team_id ?? null;
}
// For Environment models (get team_id through project)
if ($this instanceof \App\Models\Environment) {
if ($this instanceof Environment) {
return $this->project?->team_id;
}
// For database models, team is accessed through environment.project.team
if (method_exists($this, 'team')) {
if ($this instanceof \App\Models\Server) {
if ($this instanceof Server) {
$team = $this->team;
} else {
$team = $this->team();
@ -120,7 +121,6 @@ private function getTeamIdForCache()
return null;
} catch (\Throwable $e) {
// If we can't determine team ID, return null
ray('Failed to get team ID for cache: '.$e->getMessage());
return null;
}

View file

@ -82,7 +82,6 @@ protected function executeWithSshRetry(callable $callback, array $context = [],
$lastErrorMessage = '';
// Randomly fail the command with a key exchange error for testing
// if (random_int(1, 10) === 1) { // 10% chance to fail
// ray('SSH key exchange failed: kex_exchange_identification: read: Connection reset by peer');
// throw new \RuntimeException('SSH key exchange failed: kex_exchange_identification: read: Connection reset by peer');
// }
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {

View file

@ -276,7 +276,6 @@ function getGithubCommitRangeFiles(?GithubApp $source, string $owner, string $re
return $files->pluck('filename')->filter()->values()->toArray();
} catch (Exception $e) {
ray('Error fetching GitHub commit range files: '.$e->getMessage());
return [];
}
@ -302,7 +301,6 @@ function getGithubPullRequestFiles(?GithubApp $source, string $owner, string $re
return $files->pluck('filename')->filter()->values()->toArray();
} catch (Exception $e) {
ray('Error fetching GitHub PR files: '.$e->getMessage());
return [];
}

View file

@ -18,8 +18,7 @@ function send_internal_notification(string $message): void
try {
$team = Team::find(0);
$team?->notify(new GeneralNotification($message));
} catch (\Throwable $e) {
ray($e->getMessage());
} catch (Throwable) {
}
}

View file

@ -1480,9 +1480,8 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
}
}
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
} catch (Exception $e) {
} catch (Exception) {
// If parsing fails, keep the original docker_compose_raw unchanged
ray('Failed to update docker_compose_raw in applicationParser: '.$e->getMessage());
}
data_forget($resource, 'environment_variables');
@ -2732,7 +2731,6 @@ function serviceParser(Service $resource): Collection
$resource->docker_compose_raw = Yaml::dump($originalYaml, 10, 2);
} catch (Exception $e) {
// If parsing fails, keep the original docker_compose_raw unchanged
ray('Failed to update docker_compose_raw in serviceParser: '.$e->getMessage());
}
data_forget($resource, 'environment_variables');

View file

@ -1764,7 +1764,6 @@ function validateDNSEntry(string $fqdn, Server $server)
$query = new DNSQuery($dns_server);
$results = $query->query($host, $type);
if ($results === false || $query->hasError()) {
ray('Error: '.$query->getLasterror());
} else {
foreach ($results as $result) {
if ($result->getType() == $type) {
@ -3787,9 +3786,6 @@ function loggy($message = null, array $context = [])
if (! isDev()) {
return;
}
if (function_exists('ray') && config('app.debug')) {
ray($message, $context);
}
if (is_null($message)) {
return app('log');
}

View file

@ -50,7 +50,6 @@
"spatie/laravel-activitylog": "^4.11.0",
"spatie/laravel-data": "^4.19.1",
"spatie/laravel-markdown": "^2.7.1",
"spatie/laravel-ray": "^1.43.5",
"spatie/laravel-schemaless-attributes": "^2.5.1",
"spatie/url": "^2.4",
"stevebauman/purify": "^6.3.1",

792
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "64b77285a7140ce68e83db2659e9a21d",
"content-hash": "dff5cce0f3fe7ba422d9d121cce7c082",
"packages": [
{
"name": "aws/aws-crt-php",
@ -4753,134 +4753,6 @@
},
"time": "2020-10-15T08:29:30+00:00"
},
{
"name": "php-di/invoker",
"version": "2.3.7",
"source": {
"type": "git",
"url": "https://github.com/PHP-DI/Invoker.git",
"reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-DI/Invoker/zipball/3c1ddfdef181431fbc4be83378f6d036d59e81e1",
"reference": "3c1ddfdef181431fbc4be83378f6d036d59e81e1",
"shasum": ""
},
"require": {
"php": ">=7.3",
"psr/container": "^1.0|^2.0"
},
"require-dev": {
"athletic/athletic": "~0.1.8",
"mnapoli/hard-mode": "~0.3.0",
"phpunit/phpunit": "^9.0 || ^10 || ^11 || ^12"
},
"type": "library",
"autoload": {
"psr-4": {
"Invoker\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Generic and extensible callable invoker",
"homepage": "https://github.com/PHP-DI/Invoker",
"keywords": [
"callable",
"dependency",
"dependency-injection",
"injection",
"invoke",
"invoker"
],
"support": {
"issues": "https://github.com/PHP-DI/Invoker/issues",
"source": "https://github.com/PHP-DI/Invoker/tree/2.3.7"
},
"funding": [
{
"url": "https://github.com/mnapoli",
"type": "github"
}
],
"time": "2025-08-30T10:22:22+00:00"
},
{
"name": "php-di/php-di",
"version": "7.1.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-DI/PHP-DI.git",
"reference": "f88054cc052e40dbe7b383c8817c19442d480352"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/f88054cc052e40dbe7b383c8817c19442d480352",
"reference": "f88054cc052e40dbe7b383c8817c19442d480352",
"shasum": ""
},
"require": {
"laravel/serializable-closure": "^1.0 || ^2.0",
"php": ">=8.0",
"php-di/invoker": "^2.0",
"psr/container": "^1.1 || ^2.0"
},
"provide": {
"psr/container-implementation": "^1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3",
"friendsofphp/proxy-manager-lts": "^1",
"mnapoli/phpunit-easymock": "^1.3",
"phpunit/phpunit": "^9.6 || ^10 || ^11",
"vimeo/psalm": "^5|^6"
},
"suggest": {
"friendsofphp/proxy-manager-lts": "Install it if you want to use lazy injection (version ^1)"
},
"type": "library",
"autoload": {
"files": [
"src/functions.php"
],
"psr-4": {
"DI\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "The dependency injection container for humans",
"homepage": "https://php-di.org/",
"keywords": [
"PSR-11",
"container",
"container-interop",
"dependency injection",
"di",
"ioc",
"psr11"
],
"support": {
"issues": "https://github.com/PHP-DI/PHP-DI/issues",
"source": "https://github.com/PHP-DI/PHP-DI/tree/7.1.1"
},
"funding": [
{
"url": "https://github.com/mnapoli",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/php-di/php-di",
"type": "tidelift"
}
],
"time": "2025-08-16T11:10:48+00:00"
},
{
"name": "phpdocumentor/reflection-common",
"version": "2.2.0",
@ -7026,70 +6898,6 @@
},
"time": "2024-11-07T21:57:40+00:00"
},
{
"name": "spatie/backtrace",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/backtrace.git",
"reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/8ffe78be5ed355b5009e3dd989d183433e9a5adc",
"reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0"
},
"require-dev": {
"ext-json": "*",
"laravel/serializable-closure": "^1.3 || ^2.0",
"phpunit/phpunit": "^9.3 || ^11.4.3",
"spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6",
"symfony/var-dumper": "^5.1|^6.0|^7.0|^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\Backtrace\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van de Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "A better backtrace",
"homepage": "https://github.com/spatie/backtrace",
"keywords": [
"Backtrace",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/backtrace/issues",
"source": "https://github.com/spatie/backtrace/tree/1.8.2"
},
"funding": [
{
"url": "https://github.com/sponsors/spatie",
"type": "github"
},
{
"url": "https://spatie.be/open-source/support-us",
"type": "other"
}
],
"time": "2026-03-11T13:48:28+00:00"
},
{
"name": "spatie/commonmark-shiki-highlighter",
"version": "2.5.2",
@ -7462,95 +7270,6 @@
],
"time": "2026-05-19T14:06:37+00:00"
},
{
"name": "spatie/laravel-ray",
"version": "1.43.9",
"source": {
"type": "git",
"url": "https://github.com/spatie/laravel-ray.git",
"reference": "85137a6ea1d3ecd5ad3adcb43512fff9a5529e72"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-ray/zipball/85137a6ea1d3ecd5ad3adcb43512fff9a5529e72",
"reference": "85137a6ea1d3ecd5ad3adcb43512fff9a5529e72",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.2",
"ext-json": "*",
"illuminate/contracts": "^7.20|^8.19|^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/database": "^7.20|^8.19|^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/queue": "^7.20|^8.19|^9.0|^10.0|^11.0|^12.0|^13.0",
"illuminate/support": "^7.20|^8.19|^9.0|^10.0|^11.0|^12.0|^13.0",
"php": "^7.4|^8.0",
"spatie/backtrace": "^1.7.1",
"spatie/ray": "^1.45.0",
"symfony/stopwatch": "4.2|^5.1|^6.0|^7.0|^8.0",
"zbateson/mail-mime-parser": "^1.3.1|^2.0|^3.0|^4.0"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.3",
"laravel/framework": "^7.20|^8.19|^9.0|^10.0|^11.0|^12.0|^13.0",
"laravel/pint": "^1.29",
"orchestra/testbench-core": "^5.0|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"pestphp/pest": "^1.22|^2.0|^3.0|^4.0",
"phpstan/phpstan": "^1.10.57|^2.0.2",
"phpunit/phpunit": "^9.3|^10.1|^11.0.10|^12.4",
"rector/rector": "^0.19.2|^1.0.1|^2.0.0",
"spatie/pest-plugin-snapshots": "^1.1|^2.0",
"symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3|^8.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Spatie\\LaravelRay\\RayServiceProvider"
]
},
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Spatie\\LaravelRay\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Easily debug Laravel apps",
"homepage": "https://github.com/spatie/laravel-ray",
"keywords": [
"laravel-ray",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/laravel-ray/issues",
"source": "https://github.com/spatie/laravel-ray/tree/1.43.9"
},
"funding": [
{
"url": "https://github.com/sponsors/spatie",
"type": "github"
},
{
"url": "https://spatie.be/open-source/support-us",
"type": "other"
}
],
"time": "2026-04-28T06:07:04+00:00"
},
{
"name": "spatie/laravel-schemaless-attributes",
"version": "2.6.0",
@ -7757,91 +7476,6 @@
],
"time": "2026-04-28T06:26:02+00:00"
},
{
"name": "spatie/ray",
"version": "1.48.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/ray.git",
"reference": "974ac9c6e315033ab8ace883d60e094522f88ede"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/ray/zipball/974ac9c6e315033ab8ace883d60e094522f88ede",
"reference": "974ac9c6e315033ab8ace883d60e094522f88ede",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"php": "^7.4|^8.0",
"ramsey/uuid": "^3.0|^4.1",
"spatie/backtrace": "^1.7.1",
"spatie/macroable": "^1.0|^2.0",
"symfony/stopwatch": "^4.2|^5.1|^6.0|^7.0|^8.0",
"symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3|^8.0"
},
"require-dev": {
"illuminate/support": "^7.20|^8.18|^9.0|^10.0|^11.0|^12.0|^13.0",
"nesbot/carbon": "^2.63|^3.8.4",
"pestphp/pest": "^1.22",
"phpstan/phpstan": "^1.10.57|^2.0.3",
"phpunit/phpunit": "^9.5",
"rector/rector": "^0.19.2|^1.0.1|^2.0.0",
"spatie/phpunit-snapshot-assertions": "^4.2",
"spatie/test-time": "^1.2"
},
"bin": [
"bin/remove-ray.sh"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.x-dev"
}
},
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Spatie\\Ray\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van der Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "Debug with Ray to fix problems faster",
"homepage": "https://github.com/spatie/ray",
"keywords": [
"ray",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/ray/issues",
"source": "https://github.com/spatie/ray/tree/1.48.0"
},
"funding": [
{
"url": "https://github.com/sponsors/spatie",
"type": "github"
},
{
"url": "https://spatie.be/open-source/support-us",
"type": "other"
}
],
"time": "2026-03-31T12:44:31+00:00"
},
{
"name": "spatie/shiki-php",
"version": "2.4.0",
@ -9503,90 +9137,6 @@
],
"time": "2026-04-10T16:19:22+00:00"
},
{
"name": "symfony/polyfill-iconv",
"version": "v1.37.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-iconv.git",
"reference": "2c5729fd241b4b22f6e4b436bc3354a4f262df57"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/2c5729fd241b4b22f6e4b436bc3354a4f262df57",
"reference": "2c5729fd241b4b22f6e4b436bc3354a4f262df57",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-iconv": "*"
},
"suggest": {
"ext-iconv": "For best performance"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Iconv\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Iconv extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"iconv",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-iconv/tree/v1.37.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2026-04-10T16:19:22+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
"version": "v1.38.1",
@ -10922,72 +10472,6 @@
],
"time": "2026-03-28T09:44:51+00:00"
},
{
"name": "symfony/stopwatch",
"version": "v8.0.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/stopwatch.git",
"reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/stopwatch/zipball/85954ed72d5440ea4dc9a10b7e49e01df766ffa3",
"reference": "85954ed72d5440ea4dc9a10b7e49e01df766ffa3",
"shasum": ""
},
"require": {
"php": ">=8.4",
"symfony/service-contracts": "^2.5|^3"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Stopwatch\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides a way to profile code",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/stopwatch/tree/v8.0.8"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2026-03-30T15:14:47+00:00"
},
{
"name": "symfony/string",
"version": "v8.0.13",
@ -12192,214 +11676,6 @@
},
"time": "2018-08-08T15:08:14+00:00"
},
{
"name": "zbateson/mail-mime-parser",
"version": "4.0.1",
"source": {
"type": "git",
"url": "https://github.com/zbateson/mail-mime-parser.git",
"reference": "3db681988a48fdffdba551dcc6b2f4c2da574540"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zbateson/mail-mime-parser/zipball/3db681988a48fdffdba551dcc6b2f4c2da574540",
"reference": "3db681988a48fdffdba551dcc6b2f4c2da574540",
"shasum": ""
},
"require": {
"guzzlehttp/psr7": "^2.5",
"php": ">=8.1",
"php-di/php-di": "^6.0|^7.0",
"psr/log": "^1|^2|^3",
"zbateson/mb-wrapper": "^2.0 || ^3.0",
"zbateson/stream-decorators": "^2.1 || ^3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"monolog/monolog": "^2|^3",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.5"
},
"suggest": {
"ext-iconv": "For best support/performance",
"ext-mbstring": "For best support/performance"
},
"type": "library",
"autoload": {
"psr-4": {
"ZBateson\\MailMimeParser\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Zaahid Bateson"
},
{
"name": "Contributors",
"homepage": "https://github.com/zbateson/mail-mime-parser/graphs/contributors"
}
],
"description": "MIME email message parser",
"homepage": "https://mail-mime-parser.org",
"keywords": [
"MimeMailParser",
"email",
"mail",
"mailparse",
"mime",
"mimeparse",
"parser",
"php-imap"
],
"support": {
"docs": "https://mail-mime-parser.org/#usage-guide",
"issues": "https://github.com/zbateson/mail-mime-parser/issues",
"source": "https://github.com/zbateson/mail-mime-parser"
},
"funding": [
{
"url": "https://github.com/zbateson",
"type": "github"
}
],
"time": "2026-03-11T18:03:41+00:00"
},
{
"name": "zbateson/mb-wrapper",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/zbateson/mb-wrapper.git",
"reference": "f0ee6af2712e92e52ee2552588cd69d21ab3363f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zbateson/mb-wrapper/zipball/f0ee6af2712e92e52ee2552588cd69d21ab3363f",
"reference": "f0ee6af2712e92e52ee2552588cd69d21ab3363f",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/polyfill-iconv": "^1.9",
"symfony/polyfill-mbstring": "^1.9"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^10.0|^11.0"
},
"suggest": {
"ext-iconv": "For best support/performance",
"ext-mbstring": "For best support/performance"
},
"type": "library",
"autoload": {
"psr-4": {
"ZBateson\\MbWrapper\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Zaahid Bateson"
}
],
"description": "Wrapper for mbstring with fallback to iconv for encoding conversion and string manipulation",
"keywords": [
"charset",
"encoding",
"http",
"iconv",
"mail",
"mb",
"mb_convert_encoding",
"mbstring",
"mime",
"multibyte",
"string"
],
"support": {
"issues": "https://github.com/zbateson/mb-wrapper/issues",
"source": "https://github.com/zbateson/mb-wrapper/tree/3.0.0"
},
"funding": [
{
"url": "https://github.com/zbateson",
"type": "github"
}
],
"time": "2026-02-13T19:33:26+00:00"
},
{
"name": "zbateson/stream-decorators",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/zbateson/stream-decorators.git",
"reference": "0c0e79a8c960055c0e2710357098eedc07e6697a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zbateson/stream-decorators/zipball/0c0e79a8c960055c0e2710357098eedc07e6697a",
"reference": "0c0e79a8c960055c0e2710357098eedc07e6697a",
"shasum": ""
},
"require": {
"guzzlehttp/psr7": "^2.5",
"php": ">=8.1",
"zbateson/mb-wrapper": "^2.0 || ^3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "*",
"phpstan/phpstan": "*",
"phpunit/phpunit": "^10.0 || ^11.0"
},
"type": "library",
"autoload": {
"psr-4": {
"ZBateson\\StreamDecorators\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Zaahid Bateson"
}
],
"description": "PHP psr7 stream decorators for mime message part streams",
"keywords": [
"base64",
"charset",
"decorators",
"mail",
"mime",
"psr7",
"quoted-printable",
"stream",
"uuencode"
],
"support": {
"issues": "https://github.com/zbateson/stream-decorators/issues",
"source": "https://github.com/zbateson/stream-decorators/tree/3.0.0"
},
"funding": [
{
"url": "https://github.com/zbateson",
"type": "github"
}
],
"time": "2026-02-13T19:45:34+00:00"
},
{
"name": "zircote/swagger-php",
"version": "5.8.3",
@ -17401,6 +16677,70 @@
],
"time": "2026-04-16T21:33:58+00:00"
},
{
"name": "spatie/backtrace",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/spatie/backtrace.git",
"reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/backtrace/zipball/8ffe78be5ed355b5009e3dd989d183433e9a5adc",
"reference": "8ffe78be5ed355b5009e3dd989d183433e9a5adc",
"shasum": ""
},
"require": {
"php": "^7.3 || ^8.0"
},
"require-dev": {
"ext-json": "*",
"laravel/serializable-closure": "^1.3 || ^2.0",
"phpunit/phpunit": "^9.3 || ^11.4.3",
"spatie/phpunit-snapshot-assertions": "^4.2 || ^5.1.6",
"symfony/var-dumper": "^5.1|^6.0|^7.0|^8.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Spatie\\Backtrace\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Freek Van de Herten",
"email": "freek@spatie.be",
"homepage": "https://spatie.be",
"role": "Developer"
}
],
"description": "A better backtrace",
"homepage": "https://github.com/spatie/backtrace",
"keywords": [
"Backtrace",
"spatie"
],
"support": {
"issues": "https://github.com/spatie/backtrace/issues",
"source": "https://github.com/spatie/backtrace/tree/1.8.2"
},
"funding": [
{
"url": "https://github.com/sponsors/spatie",
"type": "github"
},
{
"url": "https://spatie.be/open-source/support-us",
"type": "other"
}
],
"time": "2026-03-11T13:48:28+00:00"
},
{
"name": "spatie/error-solutions",
"version": "1.1.3",
@ -18076,5 +17416,5 @@
"php": "^8.4"
},
"platform-dev": {},
"plugin-api-version": "2.9.0"
"plugin-api-version": "2.6.0"
}

View file

@ -1,108 +0,0 @@
<?php
return [
/*
* This setting controls whether data should be sent to Ray.
*
* By default, `ray()` will only transmit data in non-production environments.
*/
'enable' => env('RAY_ENABLED', true),
/*
* When enabled, all cache events will automatically be sent to Ray.
*/
'send_cache_to_ray' => env('SEND_CACHE_TO_RAY', false),
/*
* When enabled, all things passed to `dump` or `dd`
* will be sent to Ray as well.
*/
'send_dumps_to_ray' => env('SEND_DUMPS_TO_RAY', true),
/*
* When enabled all job events will automatically be sent to Ray.
*/
'send_jobs_to_ray' => env('SEND_JOBS_TO_RAY', false),
/*
* When enabled, all things logged to the application log
* will be sent to Ray as well.
*/
'send_log_calls_to_ray' => env('SEND_LOG_CALLS_TO_RAY', true),
/*
* When enabled, all queries will automatically be sent to Ray.
*/
'send_queries_to_ray' => env('SEND_QUERIES_TO_RAY', false),
/**
* When enabled, all duplicate queries will automatically be sent to Ray.
*/
'send_duplicate_queries_to_ray' => env('SEND_DUPLICATE_QUERIES_TO_RAY', false),
/*
* When enabled, slow queries will automatically be sent to Ray.
*/
'send_slow_queries_to_ray' => env('SEND_SLOW_QUERIES_TO_RAY', false),
/**
* Queries that are longer than this number of milliseconds will be regarded as slow.
*/
'slow_query_threshold_in_ms' => env('RAY_SLOW_QUERY_THRESHOLD_IN_MS', 500),
/*
* When enabled, all requests made to this app will automatically be sent to Ray.
*/
'send_requests_to_ray' => env('SEND_REQUESTS_TO_RAY', false),
/**
* When enabled, all Http Client requests made by this app will be automatically sent to Ray.
*/
'send_http_client_requests_to_ray' => env('SEND_HTTP_CLIENT_REQUESTS_TO_RAY', false),
/*
* When enabled, all views that are rendered automatically be sent to Ray.
*/
'send_views_to_ray' => env('SEND_VIEWS_TO_RAY', false),
/*
* When enabled, all exceptions will be automatically sent to Ray.
*/
'send_exceptions_to_ray' => env('SEND_EXCEPTIONS_TO_RAY', true),
/*
* When enabled, all deprecation notices will be automatically sent to Ray.
*/
'send_deprecated_notices_to_ray' => env('SEND_DEPRECATED_NOTICES_TO_RAY', false),
/*
* The host used to communicate with the Ray app.
* When using Docker on Mac or Windows, you can replace localhost with 'host.docker.internal'
* When using Docker on Linux, you can replace localhost with '172.17.0.1'
* When using Homestead with the VirtualBox provider, you can replace localhost with '10.0.2.2'
* When using Homestead with the Parallels provider, you can replace localhost with '10.211.55.2'
*/
'host' => env('RAY_HOST', 'host.docker.internal'),
/*
* The port number used to communicate with the Ray app.
*/
'port' => env('RAY_PORT', 23517),
/*
* Absolute base path for your sites or projects in Homestead,
* Vagrant, Docker, or another remote development server.
*/
'remote_path' => env('RAY_REMOTE_PATH', null),
/*
* Absolute base path for your sites or projects on your local
* computer where your IDE or code editor is running on.
*/
'local_path' => env('RAY_LOCAL_PATH', null),
/*
* When this setting is enabled, the package will not try to format values sent to Ray.
*/
'always_send_raw_values' => false,
];

View file

@ -180,7 +180,7 @@
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"RAY_ENABLED=true=123",
"FEATURE_FLAG_WITH_EQUALS=true=123",
"REGISTRY_URL=docker.io",
"SUBSCRIPTION_PROVIDER=stripe",
"TELESCOPE_ENABLED=false",
@ -295,5 +295,5 @@
}
]';
$envs = format_docker_envs_to_json($data);
$this->assertEquals('true=123', $envs->get('RAY_ENABLED'));
$this->assertEquals('true=123', $envs->get('FEATURE_FLAG_WITH_EQUALS'));
});

View file

@ -218,7 +218,7 @@ function makeAuditApplication(string $repo = 'test-org/test-repo'): Application
test('cloud provider token form does not contain debug ray calls', function () {
expect(file_get_contents(app_path('Livewire/Security/CloudProviderTokenForm.php')))
->not->toContain('ray(');
->not->toContain('ray'.'(');
});
});

View file

@ -95,6 +95,6 @@
->toContain('// This keeps the original user input clean while preventing content reapplication')
->toContain('try {')
->toContain('$originalYaml = Yaml::parse($originalCompose);')
->toContain('} catch (\Exception $e) {')
->toContain("ray('Failed to update docker_compose_raw");
->toContain('} catch (Exception) {')
->toContain('// If parsing fails, keep the original docker_compose_raw unchanged');
});

View file

@ -0,0 +1,53 @@
<?php
function rayRemovalFiles(string $directory): array
{
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory));
$files = [];
foreach ($iterator as $file) {
if ($file->isFile() && $file->getExtension() === 'php') {
$files[] = $file->getPathname();
}
}
sort($files);
return $files;
}
function rayRemovalBasePath(string $path = ''): string
{
return dirname(__DIR__, 2).($path === '' ? '' : DIRECTORY_SEPARATOR.$path);
}
it('does not include Ray as a dependency', function () {
$composer = json_decode(file_get_contents(rayRemovalBasePath('composer.json')), true, flags: JSON_THROW_ON_ERROR);
$lock = json_decode(file_get_contents(rayRemovalBasePath('composer.lock')), true, flags: JSON_THROW_ON_ERROR);
$lockedPackageNames = collect([
...($lock['packages'] ?? []),
...($lock['packages-dev'] ?? []),
])->pluck('name');
expect($composer['require'] ?? [])->not->toHaveKey('spatie/laravel-ray')
->and($composer['require-dev'] ?? [])->not->toHaveKey('spatie/laravel-ray')
->and($lockedPackageNames)->not->toContain('spatie/laravel-ray', 'spatie/ray');
});
it('does not ship Ray configuration', function () {
expect(file_exists(rayRemovalBasePath('config/ray.php')))->toBeFalse();
});
it('does not call ray from application code', function () {
$files = [
...rayRemovalFiles(rayRemovalBasePath('app')),
...rayRemovalFiles(rayRemovalBasePath('bootstrap/helpers')),
];
$rayCalls = collect($files)
->filter(fn (string $file): bool => preg_match('/\bray\s*\(/', file_get_contents($file)) === 1)
->map(fn (string $file): string => str_replace(rayRemovalBasePath().'/', '', $file))
->values();
expect($rayCalls)->toBeEmpty();
});