git-watchtower 2.3.1 → 2.3.2
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.
- package/bin/git-watchtower.js +78 -0
- package/package.json +1 -1
- package/src/server/web.js +17 -10
package/bin/git-watchtower.js
CHANGED
|
@@ -3340,6 +3340,84 @@ async function handleWebAction(action, payload) {
|
|
|
3340
3340
|
});
|
|
3341
3341
|
}
|
|
3342
3342
|
break;
|
|
3343
|
+
case 'stash': {
|
|
3344
|
+
// If a switch/pull is queued behind a dirty working tree, run the
|
|
3345
|
+
// existing stash-and-retry flow so the original operation completes
|
|
3346
|
+
// after the stash. Otherwise just stash standalone.
|
|
3347
|
+
if (pendingDirtyOperation) {
|
|
3348
|
+
const op = pendingDirtyOperation;
|
|
3349
|
+
await stashAndRetry();
|
|
3350
|
+
const label = op.type === 'switch' ? `stashed and switched to ${op.branch}` : 'stashed and pulled';
|
|
3351
|
+
sendResult(true, label);
|
|
3352
|
+
} else {
|
|
3353
|
+
const stashResult = await gitStash({ message: 'git-watchtower: stashed from web dashboard' });
|
|
3354
|
+
if (stashResult.success) {
|
|
3355
|
+
addLog('Changes stashed (from web)', 'success');
|
|
3356
|
+
telemetry.capture('stash_performed');
|
|
3357
|
+
await pollGitChanges();
|
|
3358
|
+
sendResult(true, 'Changes stashed');
|
|
3359
|
+
} else {
|
|
3360
|
+
const msg = stashResult.error ? stashResult.error.message : 'Could not stash';
|
|
3361
|
+
addLog(`Stash failed (from web): ${msg}`, 'error');
|
|
3362
|
+
sendResult(false, msg);
|
|
3363
|
+
}
|
|
3364
|
+
render();
|
|
3365
|
+
}
|
|
3366
|
+
break;
|
|
3367
|
+
}
|
|
3368
|
+
case 'stashPop': {
|
|
3369
|
+
const popResult = await gitStashPop();
|
|
3370
|
+
if (popResult.success) {
|
|
3371
|
+
addLog('Stash popped (from web)', 'success');
|
|
3372
|
+
await pollGitChanges();
|
|
3373
|
+
sendResult(true, 'Stash popped');
|
|
3374
|
+
} else {
|
|
3375
|
+
const msg = popResult.error ? popResult.error.message : 'Could not pop stash';
|
|
3376
|
+
addLog(`Stash pop failed (from web): ${msg}`, 'error');
|
|
3377
|
+
sendResult(false, msg);
|
|
3378
|
+
}
|
|
3379
|
+
render();
|
|
3380
|
+
break;
|
|
3381
|
+
}
|
|
3382
|
+
case 'deleteBranches': {
|
|
3383
|
+
const branches = Array.isArray(payload.branches) ? payload.branches : [];
|
|
3384
|
+
const force = payload.force === true;
|
|
3385
|
+
if (branches.length === 0) {
|
|
3386
|
+
sendResult(false, 'No branches specified');
|
|
3387
|
+
break;
|
|
3388
|
+
}
|
|
3389
|
+
addLog(
|
|
3390
|
+
`Cleaning up ${branches.length} branch${branches.length === 1 ? '' : 'es'}${force ? ' (force)' : ''} (from web)...`,
|
|
3391
|
+
'update'
|
|
3392
|
+
);
|
|
3393
|
+
render();
|
|
3394
|
+
const cleanupResult = await deleteGoneBranches(branches, { force });
|
|
3395
|
+
for (const name of cleanupResult.deleted) {
|
|
3396
|
+
addLog(`Deleted branch: ${name}`, 'success');
|
|
3397
|
+
}
|
|
3398
|
+
for (const f of cleanupResult.failed) {
|
|
3399
|
+
addLog(`Failed to delete ${f.name}: ${f.error}`, 'error');
|
|
3400
|
+
}
|
|
3401
|
+
if (cleanupResult.deleted.length > 0) {
|
|
3402
|
+
telemetry.capture('cleanup_branches_deleted', { count: cleanupResult.deleted.length });
|
|
3403
|
+
await pollGitChanges();
|
|
3404
|
+
}
|
|
3405
|
+
if (cleanupResult.failed.length === 0 && cleanupResult.deleted.length > 0) {
|
|
3406
|
+
sendResult(true, `Deleted ${cleanupResult.deleted.length} branch${cleanupResult.deleted.length === 1 ? '' : 'es'}`);
|
|
3407
|
+
} else if (cleanupResult.deleted.length > 0) {
|
|
3408
|
+
sendResult(
|
|
3409
|
+
false,
|
|
3410
|
+
`Deleted ${cleanupResult.deleted.length}, failed ${cleanupResult.failed.length}`
|
|
3411
|
+
);
|
|
3412
|
+
} else {
|
|
3413
|
+
sendResult(
|
|
3414
|
+
false,
|
|
3415
|
+
`Failed to delete ${cleanupResult.failed.length} branch${cleanupResult.failed.length === 1 ? '' : 'es'}`
|
|
3416
|
+
);
|
|
3417
|
+
}
|
|
3418
|
+
render();
|
|
3419
|
+
break;
|
|
3420
|
+
}
|
|
3343
3421
|
}
|
|
3344
3422
|
} catch (err) {
|
|
3345
3423
|
addLog(`Web action error: ${err.message}`, 'error');
|
package/package.json
CHANGED
package/src/server/web.js
CHANGED
|
@@ -36,6 +36,21 @@ const MAX_PORT_RETRIES = 20;
|
|
|
36
36
|
*/
|
|
37
37
|
const SSE_KEEPALIVE_INTERVAL = 15000;
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Actions the web dashboard is allowed to POST to /api/action. Every entry
|
|
41
|
+
* here MUST be matched by a `case` in `handleWebAction` in bin/git-watchtower.js
|
|
42
|
+
* — `tests/unit/server/web.test.js` enforces that link so a future addition
|
|
43
|
+
* to the whitelist can't silently no-op the way `stash` / `stashPop` /
|
|
44
|
+
* `deleteBranches` did before they were implemented.
|
|
45
|
+
*/
|
|
46
|
+
const ALLOWED_ACTIONS = Object.freeze([
|
|
47
|
+
'switchBranch', 'pull', 'fetch', 'undo',
|
|
48
|
+
'toggleSound', 'preview',
|
|
49
|
+
'restartServer', 'reloadBrowsers', 'toggleCasino',
|
|
50
|
+
'openBrowser',
|
|
51
|
+
'stash', 'stashPop', 'deleteBranches', 'checkUpdate',
|
|
52
|
+
]);
|
|
53
|
+
|
|
39
54
|
/**
|
|
40
55
|
* @typedef {Object} WebDashboardOptions
|
|
41
56
|
* @property {number} [port=4000] - Port to listen on
|
|
@@ -515,16 +530,7 @@ class WebDashboardServer {
|
|
|
515
530
|
return;
|
|
516
531
|
}
|
|
517
532
|
|
|
518
|
-
|
|
519
|
-
const allowedActions = [
|
|
520
|
-
'switchBranch', 'pull', 'fetch', 'undo',
|
|
521
|
-
'toggleSound', 'preview',
|
|
522
|
-
'restartServer', 'reloadBrowsers', 'toggleCasino',
|
|
523
|
-
'openBrowser',
|
|
524
|
-
'stash', 'stashPop', 'deleteBranches', 'checkUpdate',
|
|
525
|
-
];
|
|
526
|
-
|
|
527
|
-
if (!allowedActions.includes(action)) {
|
|
533
|
+
if (!ALLOWED_ACTIONS.includes(action)) {
|
|
528
534
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
529
535
|
res.end(JSON.stringify({ error: 'Unknown action: ' + action }));
|
|
530
536
|
return;
|
|
@@ -596,4 +602,5 @@ module.exports = {
|
|
|
596
602
|
WebDashboardServer,
|
|
597
603
|
DEFAULT_WEB_PORT,
|
|
598
604
|
STATE_PUSH_INTERVAL,
|
|
605
|
+
ALLOWED_ACTIONS,
|
|
599
606
|
};
|