git-cracked 1.2.0 → 1.4.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.
- package/README.md +93 -95
- package/package.json +2 -2
- package/src/cli.js +1 -1
- package/src/dashboard.js +626 -485
- package/src/index.js +84 -5
package/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync } from 'fs';
|
|
|
3
3
|
import { loadConfig } from './config.js';
|
|
4
4
|
import { runCommit } from './committer.js';
|
|
5
5
|
import { startDashboard } from './dashboard.js';
|
|
6
|
+
import { getActivity } from './logger.js';
|
|
6
7
|
import { CONFIG_PATH } from './paths.js';
|
|
7
8
|
|
|
8
9
|
const args = process.argv.slice(2);
|
|
@@ -21,13 +22,86 @@ if (runNow) {
|
|
|
21
22
|
process.exit(0);
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
// How often the catch-up checker runs (ms). Catches scheduled slots that were
|
|
26
|
+
// missed because the machine was asleep / shut down at the exact cron minute.
|
|
27
|
+
const CATCHUP_INTERVAL_MS = 15 * 60 * 1000;
|
|
28
|
+
|
|
24
29
|
let scheduled = false;
|
|
30
|
+
let committing = false;
|
|
31
|
+
let activeConfig = null;
|
|
25
32
|
const activeTasks = [];
|
|
33
|
+
let catchupTimer = null;
|
|
34
|
+
|
|
35
|
+
// Single-flight wrapper so the cron jobs and the catch-up checker can never
|
|
36
|
+
// run a commit at the same time (which would risk a double commit).
|
|
37
|
+
async function safeCommit(config, reason) {
|
|
38
|
+
if (committing) return;
|
|
39
|
+
committing = true;
|
|
40
|
+
try {
|
|
41
|
+
await runCommit(config);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error(`${reason} failed:`, err.message);
|
|
44
|
+
} finally {
|
|
45
|
+
committing = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Parse a 5-field cron expression into { h, m, dows } for catch-up math.
|
|
50
|
+
// Returns null for expressions with ranges/lists in the minute or hour field,
|
|
51
|
+
// which the catch-up checker simply skips (the live cron job still covers them).
|
|
52
|
+
function parseExpr(expr) {
|
|
53
|
+
const parts = expr.trim().split(/\s+/);
|
|
54
|
+
if (parts.length !== 5) return null;
|
|
55
|
+
const [min, hour, , , dow] = parts;
|
|
56
|
+
const m = parseInt(min, 10);
|
|
57
|
+
const h = parseInt(hour, 10);
|
|
58
|
+
if (Number.isNaN(m) || Number.isNaN(h)) return null;
|
|
59
|
+
|
|
60
|
+
const dows = new Set();
|
|
61
|
+
if (dow === '*') {
|
|
62
|
+
for (let i = 0; i <= 6; i++) dows.add(i);
|
|
63
|
+
} else {
|
|
64
|
+
for (const p of dow.split(',')) {
|
|
65
|
+
if (p.includes('-')) {
|
|
66
|
+
const [a, b] = p.split('-').map(Number);
|
|
67
|
+
for (let i = a; i <= b; i++) dows.add(i % 7);
|
|
68
|
+
} else {
|
|
69
|
+
dows.add(Number(p) % 7);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { h, m, dows };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// If a scheduled slot has already passed today (on a matching weekday) and no
|
|
77
|
+
// commit has happened since, commit once to catch up.
|
|
78
|
+
async function checkMissedCommits(config) {
|
|
79
|
+
const now = new Date();
|
|
80
|
+
let latestPassed = null;
|
|
81
|
+
|
|
82
|
+
for (const expr of config.schedule) {
|
|
83
|
+
const p = parseExpr(expr);
|
|
84
|
+
if (!p || !p.dows.has(now.getDay())) continue;
|
|
85
|
+
const slot = new Date(now);
|
|
86
|
+
slot.setHours(p.h, p.m, 0, 0);
|
|
87
|
+
if (slot <= now && (!latestPassed || slot > latestPassed)) latestPassed = slot;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!latestPassed) return; // nothing scheduled has passed today
|
|
91
|
+
|
|
92
|
+
const commits = getActivity().commits;
|
|
93
|
+
const last = commits[0] ? new Date(commits[0].timestamp) : null;
|
|
94
|
+
if (last && last >= latestPassed) return; // already committed since that slot
|
|
95
|
+
|
|
96
|
+
console.log(`[${now.toISOString()}] Catching up missed scheduled commit`);
|
|
97
|
+
await safeCommit(config, 'Catch-up commit');
|
|
98
|
+
}
|
|
26
99
|
|
|
27
100
|
function startSchedule(config) {
|
|
28
101
|
// Replace any previously registered jobs (e.g. after a settings change)
|
|
29
102
|
for (const task of activeTasks) task.stop();
|
|
30
103
|
activeTasks.length = 0;
|
|
104
|
+
activeConfig = config;
|
|
31
105
|
|
|
32
106
|
for (const expression of config.schedule) {
|
|
33
107
|
if (!cron.validate(expression)) {
|
|
@@ -36,16 +110,21 @@ function startSchedule(config) {
|
|
|
36
110
|
}
|
|
37
111
|
const task = cron.schedule(expression, async () => {
|
|
38
112
|
console.log(`[${new Date().toISOString()}] Cron triggered`);
|
|
39
|
-
|
|
40
|
-
await runCommit(config);
|
|
41
|
-
} catch (err) {
|
|
42
|
-
console.error('Commit failed:', err.message);
|
|
43
|
-
}
|
|
113
|
+
await safeCommit(config, 'Scheduled commit');
|
|
44
114
|
});
|
|
45
115
|
activeTasks.push(task);
|
|
46
116
|
}
|
|
117
|
+
|
|
47
118
|
scheduled = true;
|
|
48
119
|
console.log('Schedule active:', config.schedule.join(', '));
|
|
120
|
+
|
|
121
|
+
// Catch up anything missed while the machine was off, then poll periodically.
|
|
122
|
+
checkMissedCommits(config);
|
|
123
|
+
if (!catchupTimer) {
|
|
124
|
+
catchupTimer = setInterval(() => {
|
|
125
|
+
if (activeConfig) checkMissedCommits(activeConfig);
|
|
126
|
+
}, CATCHUP_INTERVAL_MS);
|
|
127
|
+
}
|
|
49
128
|
}
|
|
50
129
|
|
|
51
130
|
console.log('Git Cracked running.');
|