June 29, 2025 • 6 min read • by Katzir
One evening, after realizing my GitHub streak had dropped once again, I decided enough was enough.
I needed a tool to handle those daily “keeping-alive” commits automatically—no babysitting, no extra
dependencies. Thus, Auto Pusher was born: a small suite of scripts that fetch a free
ZenQuotes API quote, append it to quotes.txt
, and push to GitHub, all scheduled on
Windows via PowerShell.
Here’s how I organized the repo:
README.md
— Overview and instructions.env.example
— Template for your settingsauto_commit.sh
— The one Bash script that actually does the worksetup.ps1
— Installs prerequisites and creates your .env
schedule_tasks.ps1
— Registers the daily Windows Task Scheduler jobquotes.txt
— The log file where quotes accumulateREADME.md
— Your First StopThe README is your doorway. It explains:
jq
, and Windows PowerShellWhenever you need a refresher, this is the file to open:
https://github.com/ItakatzI/auto-pusher/blob/main/README.md
.env.example
— Secret Sauce
To avoid hard-coding credentials and file names, I use an environment file. Copy it to
.env
and fill in your values:
# .env.example
REPO_DIR=/c/path/to/your/repo-to-commit
REPO_ALT=/c/path/to/your/another-repo-to-commit
REPO_DIR2=C:\Users\path\to\auto-push\folder\quotesForGit
TARGET_FILE=quotes.txt
This file is ignored by Git, so your personal tokens or emails never leak.
auto_commit.sh
— The Heart in BashAlthough this project lives on Windows, I wrote the core in Bash to keep it lightweight and dependency-free. It's meant to be used on two repositories, alternating between them to contribute to two GitHub repos:
#!/usr/bin/env bash
# === Config ===
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_PATH="$SCRIPT_DIR/.env"
LOG_PATH="$SCRIPT_DIR/push_log.txt"
LAST_REPO_FILE="$SCRIPT_DIR/.last_repo"
exec > >(tee -a "$LOG_PATH") 2>&1
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting auto_commit.sh"
# Load .env
if [ -f "$ENV_PATH" ]; then
export $(grep -v '^#' "$ENV_PATH" | xargs)
else
echo "[✘] .env file not found at $ENV_PATH"
exit 1
fi
# Validate
if [ -z "$REPO_DIR" ] || [ -z "$REPO_ALT" ] || [ -z "$TARGET_FILE" ]; then
echo "[✘] Missing REPO_DIR, REPO_ALT or TARGET_FILE in .env"
exit 1
fi
# Choose repo
if [ -f "$LAST_REPO_FILE" ]; then
LAST=$(<"$LAST_REPO_FILE")
else
LAST="ALT"
fi
if [ "$LAST" = "ALT" ]; then
REPO="$REPO_DIR"
echo "DIR" >"$LAST_REPO_FILE"
else
REPO="$REPO_ALT"
echo "ALT" >"$LAST_REPO_FILE"
fi
echo "[✔] Using repo: $REPO"
cd "$REPO" || { echo "[✘] Failed to cd into $REPO"; exit 1; }
git pull
MIN_COMMITS=${MIN_COMMITS:-1}
MAX_COMMITS=2
NUM_COMMITS=$((RANDOM % (MAX_COMMITS - MIN_COMMITS + 1) + MIN_COMMITS))
echo "[✔] Planning $NUM_COMMITS motivational commits..."
for ((i=0; i> "$TARGET_FILE"
git add "$TARGET_FILE"
git commit -m "Motivation: \"$LINE\""
git checkout main
git merge daily-quote
git branch -d daily-quote
git push
done
echo "[✔] Completed $NUM_COMMITS commits."
git push origin HEAD:main
setup.ps1
— One-Click SetupRather than manually running the script, I automated it and registered the daily scheduler task:
# setup.ps1
function Load-EnvFile {
$envPath = Join-Path $PSScriptRoot ".env"
if (Test-Path $envPath) {
Get-Content $envPath | ForEach-Object {
if ($_ -match '^\s*([^#][^=]*)\s*=\s*(.*)\s*$') {
[Environment]::SetEnvironmentVariable($matches[1], $matches[2])
}
}
} else {
Write-Error "[✘] .env file not found"
exit 1
}
}
Load-EnvFile
$proj = $env:REPO_DIR2
$sched = Join-Path $proj "schedule_tasks.ps1"
$bash = Join-Path $proj "auto_commit.sh"
powershell.exe -ExecutionPolicy Bypass -File "`"$sched`""
$trig = New-ScheduledTaskTrigger -Daily -At 11:00am
$action = New-ScheduledTaskAction -Execute "powershell.exe" `
-Argument "-File `"$sched`""
Register-ScheduledTask -Action $action -Trigger $trig `
-TaskName "AutoPusherScheduler" -Force
schedule_tasks.ps1
— Wake Up and GoThis script cleans up old tasks and schedules a few random pushes each day:
# schedule_tasks.ps1
Add-Content -Path "$env:TEMP\push_scheduler_log.txt" `
-Value "[$(Get-Date)] scheduling run"
Get-Content "$PSScriptRoot\.env" | ForEach-Object {
if ($_ -match '^(.*?)=(.*)$') {
[Environment]::SetEnvironmentVariable($matches[1], $matches[2])
}
}
$repoDir = $env:REPO_DIR2
$bashPath = "C:\Program Files\Git\bin\bash.exe"
Get-ScheduledTask -TaskName "AutoPusher_*" |
Unregister-ScheduledTask -Confirm:$false
$num = Get-Random -Minimum 1 -Maximum 4
for ($i=0; $i -lt $num; $i++) {
$h = Get-Random -Minimum 20 -Maximum 22
$m = Get-Random -Minimum 0 -Maximum 59
$t = "{0:00}:{1:00}" -f $h,$m
$arg = "-c `"cd '$repoDir' && bash ./auto_commit.sh`""
New-ScheduledTaskAction -Execute $bashPath -Argument $arg |
Register-ScheduledTask -Trigger (New-ScheduledTaskTrigger -Daily -At $t) `
-TaskName "AutoPusher_$i" -Force
}
This plain text file accumulates inspirational quotes over time, building your personal archive.
bash.exe
is in your PATH or tasks will
fail.quotes.txt
; script merges
directly on main
.Dive in and try it yourself: github.com/ItakatzI/auto-pusher
← Back to all postsI'm always on the lookout for new quests—collaborate, chat, or challenge me to build the next VR dragon simulator!