Skip to content

Commit 7f6d59e

Browse files
committed
feat: enhance Bifrost commands with improved error handling and environment management
1 parent 0898258 commit 7f6d59e

File tree

8 files changed

+449
-197
lines changed

8 files changed

+449
-197
lines changed

src/Electron/Commands/Bifrost/DownloadBundleCommand.php

Lines changed: 106 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
namespace Native\Electron\Commands\Bifrost;
44

55
use Carbon\CarbonInterface;
6+
use Exception;
67
use Illuminate\Console\Command;
78
use Illuminate\Support\Facades\Http;
89
use Native\Electron\Traits\HandlesBifrost;
910
use Symfony\Component\Console\Attribute\AsCommand;
1011

1112
use function Laravel\Prompts\intro;
12-
use function Laravel\Prompts\progress;
1313

1414
#[AsCommand(
1515
name: 'bifrost:download-bundle',
@@ -23,84 +23,136 @@ class DownloadBundleCommand extends Command
2323

2424
public function handle(): int
2525
{
26-
if (! $this->checkForBifrostToken()) {
26+
try {
27+
$this->validateAuthAndGetUser();
28+
} catch (Exception $e) {
29+
$this->error($e->getMessage());
30+
$this->line('Run: php artisan bifrost:login');
31+
2732
return static::FAILURE;
2833
}
2934

3035
if (! $this->checkForBifrostProject()) {
3136
return static::FAILURE;
3237
}
3338

34-
if (! $this->checkAuthenticated()) {
35-
$this->error('Invalid API token. Please login again.');
36-
$this->line('Run: php artisan bifrost:login');
39+
intro('Fetching latest desktop bundle...');
3740

38-
return static::FAILURE;
39-
}
41+
try {
42+
$projectId = config('nativephp-internal.bifrost.project');
43+
$response = $this->makeApiRequest('GET', "api/v1/projects/{$projectId}/builds/latest-desktop-bundle");
4044

41-
intro('Fetching latest desktop bundle...');
45+
if ($response->failed()) {
46+
$this->handleApiError($response);
47+
48+
return static::FAILURE;
49+
}
50+
51+
$buildData = $response->json();
4252

43-
$projectId = config('nativephp-internal.bifrost.project');
44-
$response = Http::acceptJson()
45-
->withToken(config('nativephp-internal.bifrost.token'))
46-
->get($this->baseUrl()."api/v1/projects/{$projectId}/builds/latest-desktop-bundle");
53+
if (! isset($buildData['download_url'])) {
54+
$this->error('Bundle download URL not found in response.');
4755

48-
if ($response->failed()) {
49-
$this->handleApiError($response);
56+
return static::FAILURE;
57+
}
58+
59+
$this->displayBundleInfo($buildData);
60+
61+
$bundlePath = $this->prepareBundlePath();
62+
63+
if (! $this->downloadBundle($buildData['download_url'], $bundlePath)) {
64+
return static::FAILURE;
65+
}
66+
67+
$this->displaySuccessInfo($bundlePath);
68+
69+
return static::SUCCESS;
70+
} catch (Exception $e) {
71+
$this->error('Failed to download bundle: '.$e->getMessage());
5072

5173
return static::FAILURE;
5274
}
75+
}
5376

54-
$buildData = $response->json();
55-
$downloadUrl = $buildData['download_url'];
56-
77+
private function displayBundleInfo(array $buildData): void
78+
{
5779
$this->line('');
5880
$this->info('Bundle Details:');
59-
$this->line('Version: '.$buildData['version']);
60-
$this->line('Git Commit: '.substr($buildData['git_commit'], 0, 8));
61-
$this->line('Git Branch: '.$buildData['git_branch']);
62-
$this->line('Created: '.$buildData['created_at']);
81+
$this->line('Version: '.($buildData['version'] ?? 'Unknown'));
82+
$this->line('Git Commit: '.substr($buildData['git_commit'] ?? '', 0, 8));
83+
$this->line('Git Branch: '.($buildData['git_branch'] ?? 'Unknown'));
84+
$this->line('Created: '.($buildData['created_at'] ?? 'Unknown'));
85+
}
6386

64-
// Create build directory if it doesn't exist
87+
private function prepareBundlePath(): string
88+
{
6589
$buildDir = base_path('build');
6690
if (! is_dir($buildDir)) {
6791
mkdir($buildDir, 0755, true);
6892
}
6993

70-
$bundlePath = base_path('build/__nativephp_app_bundle');
94+
return base_path('build/__nativephp_app_bundle');
95+
}
7196

72-
// Download the bundle with progress bar
97+
private function downloadBundle(string $downloadUrl, string $bundlePath): bool
98+
{
7399
$this->line('');
74100
$this->info('Downloading bundle...');
75101

76-
$downloadResponse = Http::withOptions([
77-
'sink' => $bundlePath,
78-
'progress' => function ($downloadTotal, $downloadedBytes) {
79-
if ($downloadTotal > 0) {
80-
$progress = ($downloadedBytes / $downloadTotal) * 100;
81-
$this->output->write("\r".sprintf('Progress: %.1f%%', $progress));
82-
}
83-
},
84-
])->get($downloadUrl);
85-
86-
if ($downloadResponse->failed()) {
102+
$progressBar = $this->output->createProgressBar();
103+
$progressBar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %message%');
104+
105+
try {
106+
$downloadResponse = Http::withOptions([
107+
'sink' => $bundlePath,
108+
'progress' => function ($downloadTotal, $downloadedBytes) use ($progressBar) {
109+
if ($downloadTotal > 0) {
110+
$progressBar->setMaxSteps($downloadTotal);
111+
$progressBar->setProgress($downloadedBytes);
112+
$progressBar->setMessage(sprintf('%.1f MB', $downloadedBytes / 1024 / 1024));
113+
}
114+
},
115+
])->get($downloadUrl);
116+
117+
$progressBar->finish();
87118
$this->line('');
88-
$this->error('Failed to download bundle.');
89119

90-
if (file_exists($bundlePath)) {
91-
unlink($bundlePath);
120+
if ($downloadResponse->failed()) {
121+
$this->error('Failed to download bundle.');
122+
$this->cleanupFailedDownload($bundlePath);
123+
124+
return false;
92125
}
93126

94-
return static::FAILURE;
127+
return true;
128+
} catch (Exception $e) {
129+
$progressBar->finish();
130+
$this->line('');
131+
$this->error('Download failed: '.$e->getMessage());
132+
$this->cleanupFailedDownload($bundlePath);
133+
134+
return false;
95135
}
136+
}
96137

97-
$this->line('');
138+
private function cleanupFailedDownload(string $bundlePath): void
139+
{
140+
if (file_exists($bundlePath)) {
141+
unlink($bundlePath);
142+
$this->line('Cleaned up partial download.');
143+
}
144+
}
145+
146+
private function displaySuccessInfo(string $bundlePath): void
147+
{
98148
$this->line('');
99149
$this->info('Bundle downloaded successfully!');
100150
$this->line('Location: '.$bundlePath);
101-
$this->line('Size: '.number_format(filesize($bundlePath) / 1024 / 1024, 2).' MB');
102151

103-
return static::SUCCESS;
152+
if (file_exists($bundlePath)) {
153+
$sizeInMB = number_format(filesize($bundlePath) / 1024 / 1024, 2);
154+
$this->line("Size: {$sizeInMB} MB");
155+
}
104156
}
105157

106158
private function handleApiError($response): void
@@ -113,7 +165,15 @@ private function handleApiError($response): void
113165
$this->line('');
114166
$this->error('No desktop builds found for this project.');
115167
$this->line('');
116-
$this->info('Create a build at: '.$this->baseUrl().'{team}/desktop/projects/{project}');
168+
$teamSlug = $this->getCurrentTeamSlug();
169+
$projectId = config('nativephp-internal.bifrost.project');
170+
$baseUrl = rtrim($this->baseUrl(), '/');
171+
172+
if ($teamSlug && $projectId) {
173+
$this->info("Create a build at: {$baseUrl}/{$teamSlug}/desktop/projects/{$projectId}");
174+
} else {
175+
$this->info("Visit the dashboard: {$baseUrl}/dashboard");
176+
}
117177
break;
118178

119179
case 503:
@@ -122,14 +182,16 @@ private function handleApiError($response): void
122182
$diffMessage = $retryAfter <= 60 ? 'a minute' : $diff->diffForHumans(syntax: CarbonInterface::DIFF_ABSOLUTE);
123183
$this->line('');
124184
$this->warn('Build is still in progress.');
125-
$this->line('Please try again in '.$diffMessage.'.');
185+
$this->line("Please try again in {$diffMessage}.");
126186
break;
127187

128188
case 500:
129189
$this->line('');
130190
$this->error('Latest build has failed or was cancelled.');
131191
if (isset($data['build_id'])) {
132192
$this->line('Build ID: '.$data['build_id']);
193+
}
194+
if (isset($data['status'])) {
133195
$this->line('Status: '.$data['status']);
134196
}
135197
break;

0 commit comments

Comments
 (0)