bgrun 3.7.1 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * POST /api/deploy/:name — Git pull + install deps + restart a process
3
+ *
4
+ * Only works if the process directory is a git repository.
5
+ * Steps: git pull → bun install → force restart
6
+ */
7
+ import { getProcess } from '../../../../../src/db';
8
+ import { handleRun } from '../../../../../src/commands/run';
9
+ import { measure } from 'measure-fn';
10
+ import { $ } from 'bun';
11
+
12
+ export async function POST(req: Request, { params }: { params: { name: string } }) {
13
+ const name = decodeURIComponent(params.name);
14
+
15
+ try {
16
+ const proc = getProcess(name);
17
+ if (!proc) {
18
+ return Response.json({ error: `Process '${name}' not found` }, { status: 404 });
19
+ }
20
+
21
+ const dir = proc.workdir;
22
+
23
+ // Check if it's a git repo
24
+ const isGit = await Bun.file(`${dir}/.git/HEAD`).exists();
25
+ if (!isGit) {
26
+ return Response.json({ error: `'${dir}' is not a git repository` }, { status: 400 });
27
+ }
28
+
29
+ const result = await measure(`Deploy "${name}"`, async () => {
30
+ // 1. Git pull
31
+ $.cwd(dir);
32
+ const pullOutput = await $`git pull`.text();
33
+
34
+ // 2. Install dependencies (detect package manager)
35
+ let installOutput = '';
36
+ const hasBunLock = await Bun.file(`${dir}/bun.lock`).exists() || await Bun.file(`${dir}/bun.lockb`).exists();
37
+ const hasPackageJson = await Bun.file(`${dir}/package.json`).exists();
38
+
39
+ if (hasPackageJson) {
40
+ installOutput = await $`bun install`.text();
41
+ }
42
+
43
+ // 3. Restart the process
44
+ await handleRun({
45
+ action: 'run',
46
+ name,
47
+ force: true,
48
+ remoteName: '',
49
+ });
50
+
51
+ return { pullOutput: pullOutput.trim(), installOutput: installOutput.trim() };
52
+ });
53
+
54
+ return Response.json({ success: true, ...result });
55
+ } catch (e: any) {
56
+ return Response.json({ error: e.message }, { status: 500 });
57
+ }
58
+ }
@@ -983,6 +983,12 @@ tr.animate-in:nth-child(10) {
983
983
  border-color: var(--warning-border);
984
984
  }
985
985
 
986
+ .action-btn.deploy:hover {
987
+ background: rgba(20, 184, 166, 0.1);
988
+ color: #14b8a6;
989
+ border-color: rgba(20, 184, 166, 0.3);
990
+ }
991
+
986
992
  /* ─── Empty State ─── */
987
993
  .empty-state {
988
994
  text-align: center;
@@ -83,6 +83,17 @@ function RestartIcon() {
83
83
  );
84
84
  }
85
85
 
86
+ function DeployIcon() {
87
+ return (
88
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
89
+ <path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" />
90
+ <path d="M12 15l-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" />
91
+ <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" />
92
+ <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" />
93
+ </svg>
94
+ );
95
+ }
96
+
86
97
  // ─── Utility: Format Runtime ───
87
98
 
88
99
  function formatRuntime(raw: string): string {
@@ -180,6 +191,9 @@ function ProcessRow({ p, animate }: { p: ProcessData; animate?: boolean }) {
180
191
  <button className="action-btn warning" data-action="restart" data-name={p.name} title="Restart">
181
192
  <RestartIcon />
182
193
  </button>
194
+ <button className="action-btn deploy" data-action="deploy" data-name={p.name} title="Deploy (git pull + restart)">
195
+ <DeployIcon />
196
+ </button>
183
197
  <button className="action-btn danger" data-action="delete" data-name={p.name} title="Delete">
184
198
  <TrashIcon />
185
199
  </button>
@@ -256,6 +270,9 @@ function ProcessCard({ p }: { p: ProcessData }) {
256
270
  <button className="action-btn warning" data-action="restart" data-name={p.name} title="Restart">
257
271
  <RestartIcon /> Restart
258
272
  </button>
273
+ <button className="action-btn deploy" data-action="deploy" data-name={p.name} title="Deploy (git pull + restart)">
274
+ <DeployIcon /> Deploy
275
+ </button>
259
276
  <button className="action-btn danger" data-action="delete" data-name={p.name} title="Delete">
260
277
  <TrashIcon /> Delete
261
278
  </button>
@@ -595,6 +612,24 @@ export default function mount(): () => void {
595
612
  break;
596
613
  }
597
614
 
615
+ case 'deploy': {
616
+ showToast(`Deploying "${name}"...`, 'info');
617
+ try {
618
+ const res = await fetch(`/api/deploy/${encodeURIComponent(name)}`, { method: 'POST' });
619
+ const data = await res.json();
620
+ if (res.ok) {
621
+ showToast(`Deployed "${name}" successfully`, 'success');
622
+ } else {
623
+ showToast(data.error || `Failed to deploy "${name}"`, 'error');
624
+ }
625
+ } catch {
626
+ showToast(`Failed to deploy "${name}"`, 'error');
627
+ }
628
+ await loadProcessesFresh();
629
+ mutationUntil = Date.now() + 5000;
630
+ break;
631
+ }
632
+
598
633
  case 'logs':
599
634
  openDrawer(name);
600
635
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bgrun",
3
- "version": "3.7.1",
3
+ "version": "3.8.0",
4
4
  "description": "bgrun — A lightweight process manager for Bun",
5
5
  "type": "module",
6
6
  "main": "./src/api.ts",
@@ -57,4 +57,4 @@
57
57
  "engines": {
58
58
  "bun": ">=1.0.0"
59
59
  }
60
- }
60
+ }