1+ <?php
2+
3+ namespace Native \Electron \Commands \Bifrost ;
4+
5+ use Carbon \CarbonInterface ;
6+ use Illuminate \Console \Command ;
7+ use Illuminate \Support \Facades \Http ;
8+ use Native \Electron \Traits \HandlesBifrost ;
9+ use Symfony \Component \Console \Attribute \AsCommand ;
10+
11+ use function Laravel \Prompts \intro ;
12+ use function Laravel \Prompts \progress ;
13+
14+ #[AsCommand(
15+ name: 'bifrost:download-bundle ' ,
16+ description: 'Download the latest desktop bundle from Bifrost. ' ,
17+ )]
18+ class DownloadBundleCommand extends Command
19+ {
20+ use HandlesBifrost;
21+
22+ protected $ signature = 'bifrost:download-bundle ' ;
23+
24+ public function handle (): int
25+ {
26+ if (! $ this ->checkForBifrostToken ()) {
27+ return static ::FAILURE ;
28+ }
29+
30+ if (! $ this ->checkForBifrostProject ()) {
31+ return static ::FAILURE ;
32+ }
33+
34+ if (! $ this ->checkAuthenticated ()) {
35+ $ this ->error ('Invalid API token. Please login again. ' );
36+ $ this ->line ('Run: php artisan bifrost:login ' );
37+
38+ return static ::FAILURE ;
39+ }
40+
41+ intro ('Fetching latest desktop bundle... ' );
42+
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 " );
47+
48+ if ($ response ->failed ()) {
49+ $ this ->handleApiError ($ response );
50+
51+ return static ::FAILURE ;
52+ }
53+
54+ $ buildData = $ response ->json ();
55+ $ downloadUrl = $ buildData ['download_url ' ];
56+
57+ $ this ->line ('' );
58+ $ 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 ' ]);
63+
64+ // Create build directory if it doesn't exist
65+ $ buildDir = base_path ('build ' );
66+ if (! is_dir ($ buildDir )) {
67+ mkdir ($ buildDir , 0755 , true );
68+ }
69+
70+ $ bundlePath = base_path ('build/__nativephp_app_bundle ' );
71+
72+ // Download the bundle with progress bar
73+ $ this ->line ('' );
74+ $ this ->info ('Downloading bundle... ' );
75+
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 ()) {
87+ $ this ->line ('' );
88+ $ this ->error ('Failed to download bundle. ' );
89+
90+ if (file_exists ($ bundlePath )) {
91+ unlink ($ bundlePath );
92+ }
93+
94+ return static ::FAILURE ;
95+ }
96+
97+ $ this ->line ('' );
98+ $ this ->line ('' );
99+ $ this ->info ('Bundle downloaded successfully! ' );
100+ $ this ->line ('Location: ' .$ bundlePath );
101+ $ this ->line ('Size: ' .number_format (filesize ($ bundlePath ) / 1024 / 1024 , 2 ).' MB ' );
102+
103+ return static ::SUCCESS ;
104+ }
105+
106+ private function handleApiError ($ response ): void
107+ {
108+ $ status = $ response ->status ();
109+ $ data = $ response ->json ();
110+
111+ switch ($ status ) {
112+ case 404 :
113+ $ this ->line ('' );
114+ $ this ->error ('No desktop builds found for this project. ' );
115+ $ this ->line ('' );
116+ $ this ->info ('Create a build at: ' .$ this ->baseUrl ().'{team}/desktop/projects/{project} ' );
117+ break ;
118+
119+ case 503 :
120+ $ retryAfter = intval ($ response ->header ('Retry-After ' ));
121+ $ diff = now ()->addSeconds ($ retryAfter );
122+ $ diffMessage = $ retryAfter <= 60 ? 'a minute ' : $ diff ->diffForHumans (syntax: CarbonInterface::DIFF_ABSOLUTE );
123+ $ this ->line ('' );
124+ $ this ->warn ('Build is still in progress. ' );
125+ $ this ->line ('Please try again in ' .$ diffMessage .'. ' );
126+ break ;
127+
128+ case 500 :
129+ $ this ->line ('' );
130+ $ this ->error ('Latest build has failed or was cancelled. ' );
131+ if (isset ($ data ['build_id ' ])) {
132+ $ this ->line ('Build ID: ' .$ data ['build_id ' ]);
133+ $ this ->line ('Status: ' .$ data ['status ' ]);
134+ }
135+ break ;
136+
137+ default :
138+ $ this ->line ('' );
139+ $ this ->error ('Failed to fetch bundle: ' .($ data ['message ' ] ?? 'Unknown error ' ));
140+ }
141+ }
142+ }
0 commit comments