Stijnus commited on
Commit
07435fc
·
1 Parent(s): d479550

update fixes BETA

Browse files
app/components/@settings/tabs/update/UpdateTab.tsx CHANGED
@@ -19,6 +19,7 @@ interface UpdateProgress {
19
  totalSize?: string;
20
  currentCommit?: string;
21
  remoteCommit?: string;
 
22
  };
23
  }
24
 
@@ -113,7 +114,10 @@ const UpdateTab = () => {
113
  headers: {
114
  'Content-Type': 'application/json',
115
  },
116
- body: JSON.stringify({ branch: branchToCheck }),
 
 
 
117
  });
118
 
119
  if (!response.ok) {
@@ -152,10 +156,11 @@ const UpdateTab = () => {
152
  setIsChecking(false);
153
 
154
  if (!progress.error) {
155
- // Update was successful
156
  toast.success('Update check completed');
157
 
158
- if (progress.details?.changedFiles?.length) {
 
159
  setShowUpdateDialog(true);
160
  }
161
  }
@@ -176,6 +181,69 @@ const UpdateTab = () => {
176
  }
177
  };
178
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  return (
180
  <div className="flex flex-col gap-6">
181
  <motion.div
@@ -286,44 +354,75 @@ const UpdateTab = () => {
286
  <div className="i-ph:arrows-clockwise text-purple-500 w-5 h-5" />
287
  <h3 className="text-lg font-medium text-bolt-elements-textPrimary">Update Status</h3>
288
  </div>
289
- <button
290
- onClick={() => {
291
- setError(null);
292
- checkForUpdates();
293
- }}
294
- className={classNames(
295
- 'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
296
- 'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
297
- 'hover:bg-purple-500/10 hover:text-purple-500',
298
- 'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
299
- 'text-bolt-elements-textPrimary',
300
- 'transition-colors duration-200',
301
- 'disabled:opacity-50 disabled:cursor-not-allowed',
302
- )}
303
- disabled={isChecking}
304
- >
305
- {isChecking ? (
306
- <div className="flex items-center gap-2">
307
- <motion.div
308
- animate={{ rotate: 360 }}
309
- transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
310
- className="i-ph:arrows-clockwise w-4 h-4"
311
- />
312
- Checking...
313
- </div>
314
- ) : (
315
- <>
316
- <div className="i-ph:arrows-clockwise w-4 h-4" />
317
- Check for Updates
318
- </>
319
  )}
320
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  </div>
322
 
323
  {/* Show progress information */}
324
  {updateProgress && <UpdateProgressDisplay progress={updateProgress} />}
325
 
326
  {error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  </motion.div>
328
 
329
  {/* Update dialog */}
@@ -331,34 +430,53 @@ const UpdateTab = () => {
331
  <Dialog>
332
  <DialogTitle>Update Available</DialogTitle>
333
  <DialogDescription>
334
- {updateProgress?.details?.changedFiles && (
335
- <div className="mt-4">
336
- <p className="font-medium">Changes:</p>
337
- <ul className="list-disc list-inside mt-2">
338
- {updateProgress.details.changedFiles.map((file, index) => (
339
- <li key={index} className="text-sm">
340
- {file}
341
- </li>
342
- ))}
343
- </ul>
344
- {updateProgress.details.totalSize && (
345
- <p className="mt-2 text-sm">Total size: {updateProgress.details.totalSize}</p>
346
- )}
347
- </div>
348
- )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
  </DialogDescription>
350
- <div className="flex justify-end gap-2 mt-4">
351
  <DialogButton type="secondary" onClick={() => setShowUpdateDialog(false)}>
352
  Cancel
353
  </DialogButton>
354
- <DialogButton
355
- type="primary"
356
- onClick={() => {
357
- setShowUpdateDialog(false);
358
-
359
- // Handle update initiation here
360
- }}
361
- >
362
  Update Now
363
  </DialogButton>
364
  </div>
 
19
  totalSize?: string;
20
  currentCommit?: string;
21
  remoteCommit?: string;
22
+ updateReady?: boolean;
23
  };
24
  }
25
 
 
114
  headers: {
115
  'Content-Type': 'application/json',
116
  },
117
+ body: JSON.stringify({
118
+ branch: branchToCheck,
119
+ autoUpdate: updateSettings.autoUpdate,
120
+ }),
121
  });
122
 
123
  if (!response.ok) {
 
156
  setIsChecking(false);
157
 
158
  if (!progress.error) {
159
+ // Update check completed
160
  toast.success('Update check completed');
161
 
162
+ // Show update dialog only if there are changes and auto-update is disabled
163
+ if (progress.details?.changedFiles?.length && progress.details.updateReady) {
164
  setShowUpdateDialog(true);
165
  }
166
  }
 
181
  }
182
  };
183
 
184
+ const handleUpdate = async () => {
185
+ setShowUpdateDialog(false);
186
+
187
+ try {
188
+ const branchToCheck = isLatestBranch ? 'main' : 'stable';
189
+
190
+ // Start the update with autoUpdate set to true to force the update
191
+ const response = await fetch('/api/update', {
192
+ method: 'POST',
193
+ headers: {
194
+ 'Content-Type': 'application/json',
195
+ },
196
+ body: JSON.stringify({
197
+ branch: branchToCheck,
198
+ autoUpdate: true,
199
+ }),
200
+ });
201
+
202
+ if (!response.ok) {
203
+ throw new Error(`Update failed: ${response.statusText}`);
204
+ }
205
+
206
+ // Handle the update progress stream
207
+ const reader = response.body?.getReader();
208
+
209
+ if (!reader) {
210
+ throw new Error('No response stream available');
211
+ }
212
+
213
+ while (true) {
214
+ const { done, value } = await reader.read();
215
+
216
+ if (done) {
217
+ break;
218
+ }
219
+
220
+ const chunk = new TextDecoder().decode(value);
221
+ const lines = chunk.split('\n').filter(Boolean);
222
+
223
+ for (const line of lines) {
224
+ try {
225
+ const progress = JSON.parse(line) as UpdateProgress;
226
+ setUpdateProgress(progress);
227
+
228
+ if (progress.error) {
229
+ setError(progress.error);
230
+ toast.error('Update failed');
231
+ }
232
+
233
+ if (progress.stage === 'complete' && !progress.error) {
234
+ toast.success('Update completed successfully');
235
+ }
236
+ } catch (e) {
237
+ console.error('Error parsing update progress:', e);
238
+ }
239
+ }
240
+ }
241
+ } catch (error) {
242
+ setError(error instanceof Error ? error.message : 'Unknown error occurred');
243
+ toast.error('Update failed');
244
+ }
245
+ };
246
+
247
  return (
248
  <div className="flex flex-col gap-6">
249
  <motion.div
 
354
  <div className="i-ph:arrows-clockwise text-purple-500 w-5 h-5" />
355
  <h3 className="text-lg font-medium text-bolt-elements-textPrimary">Update Status</h3>
356
  </div>
357
+ <div className="flex items-center gap-2">
358
+ {updateProgress?.details?.updateReady && !updateSettings.autoUpdate && (
359
+ <button
360
+ onClick={handleUpdate}
361
+ className={classNames(
362
+ 'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
363
+ 'bg-purple-500 text-white',
364
+ 'hover:bg-purple-600',
365
+ 'transition-colors duration-200',
366
+ )}
367
+ >
368
+ <div className="i-ph:arrow-circle-up w-4 h-4" />
369
+ Update Now
370
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
  )}
372
+ <button
373
+ onClick={() => {
374
+ setError(null);
375
+ checkForUpdates();
376
+ }}
377
+ className={classNames(
378
+ 'flex items-center gap-2 px-4 py-2 rounded-lg text-sm',
379
+ 'bg-[#F5F5F5] dark:bg-[#1A1A1A]',
380
+ 'hover:bg-purple-500/10 hover:text-purple-500',
381
+ 'dark:hover:bg-purple-500/20 dark:hover:text-purple-500',
382
+ 'text-bolt-elements-textPrimary',
383
+ 'transition-colors duration-200',
384
+ 'disabled:opacity-50 disabled:cursor-not-allowed',
385
+ )}
386
+ disabled={isChecking}
387
+ >
388
+ {isChecking ? (
389
+ <div className="flex items-center gap-2">
390
+ <motion.div
391
+ animate={{ rotate: 360 }}
392
+ transition={{ duration: 1, repeat: Infinity, ease: 'linear' }}
393
+ className="i-ph:arrows-clockwise w-4 h-4"
394
+ />
395
+ Checking...
396
+ </div>
397
+ ) : (
398
+ <>
399
+ <div className="i-ph:arrows-clockwise w-4 h-4" />
400
+ Check for Updates
401
+ </>
402
+ )}
403
+ </button>
404
+ </div>
405
  </div>
406
 
407
  {/* Show progress information */}
408
  {updateProgress && <UpdateProgressDisplay progress={updateProgress} />}
409
 
410
  {error && <div className="mt-4 p-4 bg-red-100 text-red-700 rounded">{error}</div>}
411
+
412
+ {/* Show update source information */}
413
+ <div className="mt-4 text-sm text-bolt-elements-textSecondary">
414
+ <p>
415
+ Updates are fetched from: <span className="font-mono">stackblitz-labs/bolt.diy</span> (
416
+ {isLatestBranch ? 'main' : 'stable'} branch)
417
+ </p>
418
+ {updateProgress?.details?.currentCommit && updateProgress?.details?.remoteCommit && (
419
+ <p className="mt-1">
420
+ Current version: <span className="font-mono">{updateProgress.details.currentCommit}</span>
421
+ <span className="mx-2">→</span>
422
+ Latest version: <span className="font-mono">{updateProgress.details.remoteCommit}</span>
423
+ </p>
424
+ )}
425
+ </div>
426
  </motion.div>
427
 
428
  {/* Update dialog */}
 
430
  <Dialog>
431
  <DialogTitle>Update Available</DialogTitle>
432
  <DialogDescription>
433
+ <div className="mt-4">
434
+ <p className="text-sm text-bolt-elements-textSecondary mb-4">
435
+ A new version is available from <span className="font-mono">stackblitz-labs/bolt.diy</span> (
436
+ {isLatestBranch ? 'main' : 'stable'} branch)
437
+ </p>
438
+ {updateProgress?.details?.commitMessages && updateProgress.details.commitMessages.length > 0 && (
439
+ <div className="mb-4">
440
+ <p className="font-medium mb-2">Commit Messages:</p>
441
+ <ul className="list-disc list-inside space-y-1">
442
+ {updateProgress.details.commitMessages.map((msg, index) => (
443
+ <li key={index} className="text-sm text-bolt-elements-textSecondary">
444
+ {msg}
445
+ </li>
446
+ ))}
447
+ </ul>
448
+ </div>
449
+ )}
450
+ {updateProgress?.details?.changedFiles && (
451
+ <div>
452
+ <p className="font-medium mb-2">Changed Files:</p>
453
+ <ul className="list-disc list-inside space-y-1">
454
+ {updateProgress.details.changedFiles.map((file, index) => (
455
+ <li key={index} className="text-sm text-bolt-elements-textSecondary">
456
+ {file}
457
+ </li>
458
+ ))}
459
+ </ul>
460
+ </div>
461
+ )}
462
+ {updateProgress?.details?.totalSize && (
463
+ <p className="mt-4 text-sm text-bolt-elements-textSecondary">
464
+ Total size: {updateProgress.details.totalSize}
465
+ </p>
466
+ )}
467
+ {updateProgress?.details?.additions !== undefined && updateProgress?.details?.deletions !== undefined && (
468
+ <p className="mt-2 text-sm text-bolt-elements-textSecondary">
469
+ Changes: <span className="text-green-600">+{updateProgress.details.additions}</span>{' '}
470
+ <span className="text-red-600">-{updateProgress.details.deletions}</span>
471
+ </p>
472
+ )}
473
+ </div>
474
  </DialogDescription>
475
+ <div className="flex justify-end gap-2 mt-6">
476
  <DialogButton type="secondary" onClick={() => setShowUpdateDialog(false)}>
477
  Cancel
478
  </DialogButton>
479
+ <DialogButton type="primary" onClick={handleUpdate}>
 
 
 
 
 
 
 
480
  Update Now
481
  </DialogButton>
482
  </div>
app/routes/api.update.ts CHANGED
@@ -7,6 +7,7 @@ const execAsync = promisify(exec);
7
 
8
  interface UpdateRequestBody {
9
  branch: string;
 
10
  }
11
 
12
  interface UpdateProgress {
@@ -22,6 +23,7 @@ interface UpdateProgress {
22
  totalSize?: string;
23
  currentCommit?: string;
24
  remoteCommit?: string;
 
25
  };
26
  }
27
 
@@ -37,7 +39,7 @@ export const action: ActionFunction = async ({ request }) => {
37
  return json({ error: 'Invalid request body: branch is required and must be a string' }, { status: 400 });
38
  }
39
 
40
- const { branch } = body as UpdateRequestBody;
41
 
42
  // Create a ReadableStream to send progress updates
43
  const stream = new ReadableStream({
@@ -271,9 +273,30 @@ export const action: ActionFunction = async ({ request }) => {
271
  totalSize: formatSize(totalSizeInBytes),
272
  currentCommit: currentCommit.trim().substring(0, 7),
273
  remoteCommit: remoteCommit.trim().substring(0, 7),
 
274
  },
275
  });
276
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
277
  // Pull stage
278
  sendProgress({
279
  stage: 'pull',
 
7
 
8
  interface UpdateRequestBody {
9
  branch: string;
10
+ autoUpdate?: boolean;
11
  }
12
 
13
  interface UpdateProgress {
 
23
  totalSize?: string;
24
  currentCommit?: string;
25
  remoteCommit?: string;
26
+ updateReady?: boolean;
27
  };
28
  }
29
 
 
39
  return json({ error: 'Invalid request body: branch is required and must be a string' }, { status: 400 });
40
  }
41
 
42
+ const { branch, autoUpdate = false } = body as UpdateRequestBody;
43
 
44
  // Create a ReadableStream to send progress updates
45
  const stream = new ReadableStream({
 
273
  totalSize: formatSize(totalSizeInBytes),
274
  currentCommit: currentCommit.trim().substring(0, 7),
275
  remoteCommit: remoteCommit.trim().substring(0, 7),
276
+ updateReady: true,
277
  },
278
  });
279
 
280
+ // Only proceed with update if autoUpdate is true
281
+ if (!autoUpdate) {
282
+ sendProgress({
283
+ stage: 'complete',
284
+ message: 'Update is ready to be applied. Click "Update Now" to proceed.',
285
+ progress: 100,
286
+ details: {
287
+ changedFiles,
288
+ additions: stats?.[2] ? parseInt(stats[2]) : 0,
289
+ deletions: stats?.[3] ? parseInt(stats[3]) : 0,
290
+ commitMessages,
291
+ totalSize: formatSize(totalSizeInBytes),
292
+ currentCommit: currentCommit.trim().substring(0, 7),
293
+ remoteCommit: remoteCommit.trim().substring(0, 7),
294
+ updateReady: true,
295
+ },
296
+ });
297
+ return;
298
+ }
299
+
300
  // Pull stage
301
  sendProgress({
302
  stage: 'pull',