• I wasted a shameful amount of hours trying to automate pushing to git using a Mac every set interval. I did learn a few things so it wasn’t all a waste. Also to be fair I do enjoy automating things and going through this will definitely make it much easier for me to automate new things in the future (AI +Automations😋).
  • Nonetheless, in the hopes that no one else goes through my struggle, here’s what I learned

Learned

General Advice

Bash

  • Bash script is a pleasant scripting language that is under-appreciated by many new devs
  • You can see the error code of the previous command you ran by running echo $?
    • 0 is success (truthy)
    • 1 is fail (falsy)
    • I used that in here:
# Check if there are changes to commit
if git diff-index --quiet HEAD --; then
    echo "No changes to commit"
else
    echo "Found changes!"

Terminal

  • tail is goated. especially if you are trying to listen to several files. As I was debugging my launchd, I had: tail -f /tmp/git_push.out tail -f /tmp/git_push.err running in one terminal. This gave me the ability to continue reading the most recent content that was added to the log as I tested the automation

Git

You can disable things like 1password verification by running git config --local commit.gpgSign false in the repository that you are targeting. I had to do that because when running the script using launchd, it fails to prompt the user to verify their fingerprint so the script just fails. Error code on the automation was 127 or 128 iirc.

Launchd

  • Scripts run by launchd may operate in a different environment than those run in an interactive shell. Story of how I came to realize that:
    • TLDR: add source /Users/$(whoami)/.zshrc to your bash script to make it work normally when running through Launchd
    • I wasted a shameful amount of hours trying to figure out why I couldn’t push using the script. I thought I had to provide a personal access token but I am not using an SDK or external API/server, I AM USING BASH IN MY LOCAL MACHINE… But that fact just flew over my head
    • After realizing it’s not coz of git permissions. I started investigating the difference between running the scripts manually and using the LaunchAgent that runs my script. That made me realize a few odd things:
      • echo "$(ls)" doesn’t print out anything when running it through launchd.
      • This gave me the suspicion that git was also not even being run properly
      • After a little bit of chatting with chatGPT, I kept on seeing that they kept on exporting the PATH. That made me think of the .zshrc. With excitement, I simply added source /Users/$(whoami)/.zshrc at the top of my bash file. To my surprise, that’s ALL I HAD TO DO to make git work in bash scripts being run by launchd.
      • unfortunately, it was not possible to source .zshrc inside the launchd file so I had to put everything in the bash script.
  • Adding these are crucial for debugging launchd automations:
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/git_push.out</string>
    <key>StandardErrorPath</key>
    <string>/tmp/git_push.err</string>

Code

Bash (sh)

/Users/rami/bin/git_push.sh

#!/bin/bash
source /Users/$(whoami)/.zshrc
 
echo "Argument received: $1"
# Navigate to the specified directory
cd "$1" || {
    echo "Failed to change directory to $1"
    exit 1
}
 
git add .
 
# Check if there are changes to commit
if git diff-index --quiet HEAD --; then
    echo "No changes to commit"
else
    echo "Found changes!"
    git commit -m "Daily commit: $(date)"
    # Push changes to the remote repository
    if git push; then
        echo "Changes pushed!"
    else
        echo "Failed to push changes"
        exit 1
    fi
fi

Launchd (plist)

/Users/rami/Library/LaunchAgents/com.psycho-baller.git_push.notes.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.psycho-baller.git_push.notes</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/rami/bin/git_push.sh</string>
	    <string>/Users/rami/Documents/Obsidian</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/git_push.out</string>
    <key>StandardErrorPath</key>
    <string>/tmp/git_push.err</string>
</dict>
</plist>

Faced more errors using a different mac. This is the updated files that fixes the errors:

/Users/rami/bin/git_push.sh

#!/bin/zsh
 
echo "Argument received: $1"
 
# Navigate to the specified directory
cd "$1" || {
	echo "Failed to change directory to $1"
	exit 1
}
 
/opt/homebrew/bin/git add .
# Check if there are changes to commit
if /opt/homebrew/bin/git diff-index --quiet HEAD --; then
	echo "No changes to commit"
else
	echo "Found changes!"
	/opt/homebrew/bin/git commit -m "Daily commit: $(date)"
 
	# Update submodules and sync with Quartz to deploy changes
	cd /Users/rami/Documents/code/my-universe || exit
	/opt/homebrew/bin/git submodule update --remote --merge
	/Users/rami/.asdf/shims/npx quartz sync
# Push changes to the remote repository
if /opt/homebrew/bin/git push; then
	echo "Changes pushed!"
	else
		echo "Failed to push changes"
	exit 1
	fi
fi
 

/Users/rami/Library/LaunchAgents/com.psycho-baller.git_push.notes.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.psycho-baller.git_push.notes</string>
    <key>ProgramArguments</key>
	<array>
		<string>/Users/rami/bin/git_push.sh</string>
		<string>/Users/rami/Documents/Obsidian</string>
</array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>9</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>RunAtLoad</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/git_push.out</string>
    <key>StandardErrorPath</key>
    <string>/tmp/git_push.err</string>
</dict>
</plist>