26.0.2).'; public function handle(): int { $current = Setting::query()->where('key', 'version')->value('value'); if (!$current) { $this->error('Unable to determine current version from settings.'); return self::FAILURE; } $next = $this->bumpPatch($current); if ($next === null) { $this->error('Version format must be X.Y.Z (optionally with suffix).'); return self::FAILURE; } Setting::updateOrCreate(['key' => 'version'], ['value' => $next]); if (!$this->syncComposerVersion($next)) { $this->error('Failed to sync version to composer.json.'); return self::FAILURE; } $this->info("Version bumped: {$current} -> {$next}"); return self::SUCCESS; } private function bumpPatch(string $version): ?string { if (!preg_match('/^(\d+)\.(\d+)\.(\d+)(.*)?$/', $version, $matches)) { return null; } $major = $matches[1]; $minor = $matches[2]; $patch = $matches[3]; $suffix = $matches[4] ?? ''; $patchWidth = strlen($patch); $nextPatch = (string) ((int) $patch + 1); if ($patchWidth > 1) { $nextPatch = str_pad($nextPatch, $patchWidth, '0', STR_PAD_LEFT); } return "{$major}.{$minor}.{$nextPatch}{$suffix}"; } private function syncComposerVersion(string $version): bool { $composerPath = base_path('composer.json'); if (!is_file($composerPath) || !is_readable($composerPath)) { return false; } $raw = file_get_contents($composerPath); if ($raw === false) { return false; } $data = json_decode($raw, true); if (!is_array($data)) { return false; } $data['version'] = $version; $encoded = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); if ($encoded === false) { return false; } $encoded .= "\n"; return file_put_contents($composerPath, $encoded) !== false; } }