git-cracked 1.3.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 +1 -1
- package/src/index.js +84 -5
package/README.md
CHANGED
|
@@ -1,26 +1,68 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# git-cracked
|
|
2
4
|
|
|
3
|
-
Keep your GitHub contribution graph green
|
|
5
|
+
**Keep your GitHub contribution graph green — automatically.**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/git-cracked)
|
|
8
|
+
[](https://nodejs.org)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](#)
|
|
11
|
+
|
|
12
|
+
[](https://github.com/trentzx/git-cracked/stargazers)
|
|
13
|
+
[](https://github.com/trentzx/git-cracked/network/members)
|
|
14
|
+
[](https://github.com/trentzx/git-cracked/issues)
|
|
15
|
+
[](https://github.com/trentzx/git-cracked/pulls)
|
|
16
|
+
|
|
17
|
+
git-cracked makes small, realistic-looking code changes to a private repo on a
|
|
18
|
+
schedule and commits them for you — so your activity graph stays active without
|
|
19
|
+
you lifting a finger. Free, local, and no API key required.
|
|
20
|
+
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<table>
|
|
24
|
+
<tr>
|
|
25
|
+
<td valign="top" width="50%">
|
|
26
|
+
|
|
27
|
+
**What it does**
|
|
28
|
+
|
|
29
|
+
- Commits on a schedule (e.g. 9am, 1pm, 4pm weekdays)
|
|
30
|
+
- Makes realistic code changes across 18 languages
|
|
31
|
+
- Generates matching commit messages
|
|
32
|
+
- Catches up missed commits after sleep/shutdown
|
|
33
|
+
- Pushes to your private repo automatically
|
|
34
|
+
|
|
35
|
+
</td>
|
|
36
|
+
<td valign="top" width="50%">
|
|
4
37
|
|
|
5
|
-
|
|
38
|
+
**What it uses**
|
|
6
39
|
|
|
7
|
-
|
|
40
|
+
- Node.js (cross-platform: Win / macOS / Linux)
|
|
41
|
+
- `simple-git` for git operations
|
|
42
|
+
- `node-cron` for scheduling
|
|
43
|
+
- A built-in local web dashboard
|
|
44
|
+
- Zero external services or API keys
|
|
8
45
|
|
|
9
|
-
|
|
46
|
+
</td>
|
|
47
|
+
</tr>
|
|
48
|
+
</table>
|
|
10
49
|
|
|
11
|
-
|
|
12
|
-
- A GitHub account
|
|
13
|
-
- A private repo cloned to your machine (with some source code files in it)
|
|
50
|
+
## Quick Start
|
|
14
51
|
|
|
15
|
-
|
|
52
|
+
One command — it opens your browser to a 30-second setup form:
|
|
16
53
|
|
|
17
54
|
```bash
|
|
18
55
|
npx git-cracked
|
|
19
56
|
```
|
|
20
57
|
|
|
21
|
-
|
|
58
|
+
On the setup page, click **+** to browse for your private repo folder (or paste
|
|
59
|
+
the path), pick a schedule, and save. That's the whole install.
|
|
22
60
|
|
|
23
|
-
|
|
61
|
+
> **You need:** Node.js 18+, a GitHub account, and a **private** repo cloned
|
|
62
|
+
> locally with a few source files in it. Don't have one?
|
|
63
|
+
> [Create one on GitHub](https://github.com/new).
|
|
64
|
+
|
|
65
|
+
### Install from source instead
|
|
24
66
|
|
|
25
67
|
```bash
|
|
26
68
|
git clone https://github.com/trentzx/git-cracked.git
|
|
@@ -29,40 +71,30 @@ npm install
|
|
|
29
71
|
npm start
|
|
30
72
|
```
|
|
31
73
|
|
|
32
|
-
###
|
|
33
|
-
|
|
34
|
-
From a source install:
|
|
74
|
+
### Keep it running after reboots
|
|
35
75
|
|
|
36
76
|
```bash
|
|
37
|
-
#
|
|
38
|
-
npm run install-windows
|
|
39
|
-
|
|
40
|
-
# macOS
|
|
41
|
-
npm run install-mac
|
|
42
|
-
|
|
43
|
-
# Linux
|
|
44
|
-
npm run install-linux
|
|
77
|
+
npm run install-windows # or: install-mac / install-linux
|
|
45
78
|
```
|
|
46
79
|
|
|
47
|
-
|
|
80
|
+
This registers git-cracked to start automatically on login and run quietly in
|
|
81
|
+
the background.
|
|
48
82
|
|
|
49
83
|
## Dashboard
|
|
50
84
|
|
|
51
|
-
Once running, open **[
|
|
85
|
+
Once running, open **[localhost:4856](http://localhost:4856)** to see:
|
|
52
86
|
|
|
53
|
-
- Total commits
|
|
54
|
-
- Next scheduled commit times with countdown
|
|
55
|
-
- Full commit history
|
|
56
|
-
-
|
|
57
|
-
- Settings page to change
|
|
87
|
+
- Total commits — all time and today
|
|
88
|
+
- Next scheduled commit times with a live countdown
|
|
89
|
+
- Full commit history (message, file changed, when)
|
|
90
|
+
- A **⚡ Commit now** button to fire one instantly
|
|
91
|
+
- A **Settings** page to change repo, branch, or schedule
|
|
58
92
|
|
|
59
93
|
The dashboard auto-refreshes every 30 seconds.
|
|
60
94
|
|
|
61
|
-
|
|
95
|
+
## Schedule
|
|
62
96
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Go to [http://localhost:4856/settings](http://localhost:4856/settings) and pick from the dropdown:
|
|
97
|
+
Change it any time on the [Settings page](http://localhost:4856/settings):
|
|
66
98
|
|
|
67
99
|
| Option | Times |
|
|
68
100
|
|---|---|
|
|
@@ -71,86 +103,52 @@ Go to [http://localhost:4856/settings](http://localhost:4856/settings) and pick
|
|
|
71
103
|
| 4× per weekday | 9am, 12pm, 3pm, 6pm |
|
|
72
104
|
| 1× per weekday | 10am |
|
|
73
105
|
|
|
74
|
-
All times use your computer's local time zone, so it works the same anywhere in
|
|
75
|
-
|
|
76
|
-
|
|
106
|
+
All times use your computer's local time zone, so it works the same anywhere in
|
|
107
|
+
the world. If your machine is asleep at a scheduled time, git-cracked commits as
|
|
108
|
+
soon as it wakes up so you don't miss a day.
|
|
77
109
|
|
|
78
110
|
## How it works
|
|
79
111
|
|
|
80
112
|
1. At each scheduled time, a random source file in your repo is selected
|
|
81
|
-
2. A small, realistic change is applied —
|
|
82
|
-
|
|
83
|
-
|
|
113
|
+
2. A small, realistic change is applied — whitespace, quote style, `let`→`const`,
|
|
114
|
+
strict equality, import sorting, an adjusted constant, etc.
|
|
115
|
+
3. A matching message is chosen from a bank of 130+ conventional commit messages
|
|
116
|
+
4. The change is committed and pushed to your private repo
|
|
84
117
|
|
|
85
|
-
**Supported file types:** `.js` `.ts` `.jsx` `.tsx` `.mjs` `.py` `.go` `.java`
|
|
118
|
+
**Supported file types:** `.js` `.ts` `.jsx` `.tsx` `.mjs` `.py` `.go` `.java`
|
|
119
|
+
`.rb` `.cs` `.cpp` `.c` `.rs` `.php` `.swift` `.kt` `.scala` `.sh`
|
|
86
120
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
## Stopping / removing
|
|
121
|
+
## Show private commits on your graph
|
|
90
122
|
|
|
91
|
-
|
|
123
|
+
Commits to private repos don't appear on your public graph by default. To count
|
|
124
|
+
them: profile → **Contribution settings** → enable **Private contributions**.
|
|
125
|
+
Visitors will see the activity, not the repo or code.
|
|
92
126
|
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
del "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\git-cracked.vbs"
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
**macOS:**
|
|
99
|
-
```bash
|
|
100
|
-
launchctl unload ~/Library/LaunchAgents/com.gitcracked.autocommit.plist
|
|
101
|
-
rm ~/Library/LaunchAgents/com.gitcracked.autocommit.plist
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Linux:**
|
|
105
|
-
```bash
|
|
106
|
-
systemctl --user disable --now git-cracked
|
|
107
|
-
```
|
|
127
|
+
## Stopping / removing
|
|
108
128
|
|
|
109
|
-
|
|
129
|
+
| Platform | Command |
|
|
130
|
+
|---|---|
|
|
131
|
+
| Windows | `del "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\git-cracked.vbs"` |
|
|
132
|
+
| macOS | `launchctl unload ~/Library/LaunchAgents/com.gitcracked.autocommit.plist && rm ~/Library/LaunchAgents/com.gitcracked.autocommit.plist` |
|
|
133
|
+
| Linux | `systemctl --user disable --now git-cracked` |
|
|
110
134
|
|
|
111
135
|
## Troubleshooting
|
|
112
136
|
|
|
113
|
-
**"No suitable files found to mutate"**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
**Commits don't show on my GitHub graph**
|
|
117
|
-
Make sure `git config user.email` in your private repo matches your GitHub account email:
|
|
118
|
-
```bash
|
|
119
|
-
cd path/to/your/private/repo
|
|
120
|
-
git config user.email "you@example.com"
|
|
121
|
-
```
|
|
137
|
+
**"No suitable files found to mutate"** — your repo needs real source files
|
|
138
|
+
(`.js`, `.py`, etc.) that are at least 5 lines long and not minified.
|
|
122
139
|
|
|
123
|
-
**
|
|
124
|
-
|
|
140
|
+
**Commits don't show on my graph** — make sure `git config user.email` in your
|
|
141
|
+
repo matches your GitHub account email, and enable private contributions (above).
|
|
125
142
|
|
|
126
|
-
|
|
143
|
+
**Push fails** — you need GitHub credentials for HTTPS. Run `gh auth login` or
|
|
144
|
+
set up SSH keys.
|
|
127
145
|
|
|
128
146
|
## Security
|
|
129
147
|
|
|
130
|
-
-
|
|
131
|
-
-
|
|
148
|
+
- Settings live in `~/.git-cracked/` on your machine and are never uploaded
|
|
149
|
+
- The dashboard binds to localhost only and rejects cross-origin requests
|
|
132
150
|
- The tool only touches files inside the repo path you configure
|
|
133
151
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
## Project structure
|
|
152
|
+
## License
|
|
137
153
|
|
|
138
|
-
|
|
139
|
-
git-cracked/
|
|
140
|
-
├── src/
|
|
141
|
-
│ ├── cli.js Entry point — starts app and opens browser
|
|
142
|
-
│ ├── index.js Scheduler
|
|
143
|
-
│ ├── dashboard.js Web dashboard + setup wizard (port 4856)
|
|
144
|
-
│ ├── committer.js Runs mutate → commit → push
|
|
145
|
-
│ ├── mutator.js Applies realistic code changes
|
|
146
|
-
│ ├── messages.js 130+ commit message bank
|
|
147
|
-
│ ├── logger.js Saves activity to activity.json
|
|
148
|
-
│ ├── config.js Loads and validates config.json
|
|
149
|
-
│ └── setup.js CLI setup wizard (alternative to web UI)
|
|
150
|
-
├── scripts/
|
|
151
|
-
│ ├── install-windows.js
|
|
152
|
-
│ ├── install-mac.js
|
|
153
|
-
│ └── install-linux.js
|
|
154
|
-
├── config.example.json
|
|
155
|
-
└── package.json
|
|
156
|
-
```
|
|
154
|
+
MIT © [Trenton Scott](https://github.com/trentzx)
|
package/package.json
CHANGED
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.');
|