Files
tkb_timeshift/watch-and-commit.js
Docker Config Backup 8ed2967f6f Save before creating restore point: Session start
Auto-saved at 2025-07-29 14:27:28

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-29 14:27:28 +02:00

189 lines
6.7 KiB
JavaScript
Executable File

#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
// Configuration
const WATCH_DIRS = ['components', 'app', 'lib', 'styles'];
const WATCH_EXTENSIONS = ['.tsx', '.ts', '.js', '.jsx', '.css', '.scss', '.json'];
const COMMIT_DELAY = 30000; // 30 seconds delay before auto-commit
const MAX_COMMITS_PER_HOUR = 10;
class AutoCommitWatcher {
constructor() {
this.commitQueue = new Set();
this.commitTimeout = null;
this.commitsThisHour = 0;
this.lastCommitTime = 0;
this.startTime = new Date();
// Reset commit counter every hour
setInterval(() => {
this.commitsThisHour = 0;
console.log('🔄 Hourly reset: Commit counter reset to 0');
}, 3600000);
// Log startup info
console.log('🚀 Timeshift Auto-Commit Watcher Started');
console.log('==========================================');
console.log(`📅 Start time: ${this.startTime.toISOString()}`);
console.log(`📁 Watching directories: ${WATCH_DIRS.join(', ')}`);
console.log(`📄 File extensions: ${WATCH_EXTENSIONS.join(', ')}`);
console.log(`⏱️ Commit delay: ${COMMIT_DELAY/1000}s`);
console.log(`🚦 Max commits per hour: ${MAX_COMMITS_PER_HOUR}`);
console.log(`🔧 Process ID: ${process.pid}`);
console.log('==========================================');
}
start() {
WATCH_DIRS.forEach(dir => {
const fullPath = path.join(process.cwd(), dir);
if (fs.existsSync(fullPath)) {
this.watchDirectory(fullPath);
console.log(`👀 Watching: ${fullPath}`);
} else {
console.log(`⚠️ Directory not found: ${fullPath}`);
}
});
}
watchDirectory(dirPath) {
try {
fs.watch(dirPath, { recursive: true }, (eventType, filename) => {
if (!filename) return;
const filePath = path.join(dirPath, filename);
const ext = path.extname(filename);
// Check if file extension is in our watch list
if (WATCH_EXTENSIONS.includes(ext)) {
this.handleFileChange(filePath, eventType);
}
});
} catch (error) {
console.error(`❌ Error watching directory ${dirPath}:`, error.message);
}
}
handleFileChange(filePath, eventType) {
const relativePath = path.relative(process.cwd(), filePath);
console.log(`📝 File changed: ${relativePath} (${eventType})`);
// Add to commit queue
this.commitQueue.add(relativePath);
// Clear existing timeout and set new one
if (this.commitTimeout) {
clearTimeout(this.commitTimeout);
}
this.commitTimeout = setTimeout(() => {
this.performAutoCommit();
}, COMMIT_DELAY);
}
async performAutoCommit() {
// Rate limiting
if (this.commitsThisHour >= MAX_COMMITS_PER_HOUR) {
console.log(`⏸️ Rate limit reached (${MAX_COMMITS_PER_HOUR} commits/hour). Skipping auto-commit.`);
this.commitQueue.clear();
return;
}
// Check if enough time has passed since last commit
const now = Date.now();
if (now - this.lastCommitTime < COMMIT_DELAY) {
console.log('⏸️ Too soon since last commit. Skipping.');
return;
}
if (this.commitQueue.size === 0) {
console.log('📋 No files in commit queue');
return;
}
try {
// Check if there are actually changes to commit
const { stdout: status } = await execAsync('git status --porcelain');
if (!status.trim()) {
console.log('📝 No actual changes to commit');
this.commitQueue.clear();
return;
}
console.log('🔄 Performing auto-commit...');
// Create commit message with changed files
const changedFiles = Array.from(this.commitQueue).slice(0, 5); // Limit to 5 files in message
const fileList = changedFiles.join(', ');
const moreFiles = this.commitQueue.size > 5 ? ` and ${this.commitQueue.size - 5} more` : '';
const message = `Auto-save: Updated ${fileList}${moreFiles}`;
// Use our auto-commit script
await execAsync(`./auto-commit.sh commit "${message}"`);
console.log('✅ Auto-commit successful');
this.commitsThisHour++;
this.lastCommitTime = now;
this.commitQueue.clear();
} catch (error) {
console.error('❌ Auto-commit failed:', error.message);
}
}
async createRestorePoint(description = 'Auto restore point') {
try {
console.log('🔖 Creating restore point...');
await execAsync(`./auto-commit.sh restore-point "${description}"`);
console.log('✅ Restore point created');
} catch (error) {
console.error('❌ Failed to create restore point:', error.message);
}
}
}
// Handle process termination gracefully
process.on('SIGINT', async () => {
console.log('\n🛑 Stopping auto-commit watcher...');
// Perform final commit if there are pending changes
try {
const { stdout: status } = await execAsync('git status --porcelain');
if (status.trim()) {
console.log('💾 Saving final changes...');
await execAsync('./auto-commit.sh commit "Final auto-save before shutdown"');
}
} catch (error) {
console.error('❌ Error during shutdown save:', error.message);
}
console.log('👋 Auto-commit watcher stopped');
process.exit(0);
});
// Start the watcher
const watcher = new AutoCommitWatcher();
watcher.start();
// Create restore point on startup
setTimeout(() => {
watcher.createRestorePoint('Session start');
}, 5000);
// Create periodic restore points (every 30 minutes)
setInterval(() => {
watcher.createRestorePoint('Periodic backup');
}, 30 * 60 * 1000);
console.log('🚀 Auto-commit system ready! Press Ctrl+C to stop.');
console.log('📋 Commands available:');
console.log(' ./auto-commit.sh commit [message]');
console.log(' ./auto-commit.sh restore-point [description]');
console.log(' ./auto-commit.sh list');
console.log(' ./auto-commit.sh cleanup');