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