[Git + mac]: Schedule your git commands with launchd
git
mac
03/21/2020
launchd
In computing, launchd, a unified operating system service management framework, starts, stops and manages daemons, applications, processes, and scripts in macOS. It was introduced with Mac OS X Tiger and is licensed under the Apache License — Wikipedia
Scenario Example
You have completed a new release for your project that should be pushed to your GitHub repository at 11:11AM tomorrow. But for some reason, you won't be able to be sitting in front of your computer to upload your release. Let's see how we can schedule to run a program (script) while you're away.
Write a script that needs to be run at a certain time
~/Desktop/push_release.sh
cd ~/Desktop/projectgit add .git commit -m "[RELEASE]: 1.0.1"git pushIf you want to create a log file as a result, you can put output to file
cd ~/Desktop/projectgit add .git commit -m "[RELEASE]: 1.0.1" > /tmp/commit_log 2>&1git push > /tmp/push_log 2>&1
2&>1indicates that standard error is redirected to specified file
(optional): you can choose to make *.sh executable with chmod +x *.md, then you won't have to specify bash in ProgramArguments in plist file below.
Create plist to trigger program to run at a specific time
Make sure to place it under ~/Library/LaunchAgents. ~ denotes home directory.
~/Library/LaunchAgents/com.launchd.example.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.launchd.example</string> <key>ProgramArguments</key> <array> <string>bash</string> <!-- This line isn't required if *.sh is already executable --> <string>/Users/Me/Desktop/push_release.sh</string> </array> <key>StartCalendarInterval</key> <dict> <key>Month</key> <integer>3</integer> <key>Day</key> <integer>22</integer> <key>Hour</key> <integer>11</integer> <key>Minute</key> <integer>11</integer> </dict> </dict></plist>Runs
bash /Users/Me/Desktop/push_release.shon3/22 at 11:11AMPlease read More about launchd to learn more details
Load launchd
You need to load the plist file after it's created.
launchctl load com.launchd.example.plistTo verify that it's loaded
launchctl list | grep com.launchd.example- 0 com.lanchd.exampleReceiving output, i.e.
- 0 com.launchd.examplemeans that the job is currently loaded
-: means that it is loaded but is not running
0means job finished successfully. Any positive number means that there was an error, and negative number denotes a termination
To unload the job
launchctl unload com.launchd.example.plistBe Aware..
Loaded launchd will still work with Terminal.app closed, but it won't work while your computer is asleep or shutdown.
If it wakes up before the job was executed, or there is more job to be executed, it will continue to work without having to "re-loading" the job
If it wakes up after invocation time period time, and there is no more job to run, it won't re-execute
One workaround can be waking up the mac a few minutes before execution time by going to
Preference>>Energy Saver>>Schedule
More about launchd
Directory
Below describes each directory where you can choose to place your plist scripts
| Type | Location | Who Runs |
|---|---|---|
| User Agents* | ~/Library/LaunchAgents | Currently signed in user |
| Global Daemons | /Library/LaunchDaemons (Use sudo to edit files) | Currently signed in user |
| Global Agents | /Library/LaunchAgents (Use sudo to edit files) | root or specified user with Username key |
In short, place the property list under
~/Library/LaunchAgentsfor user specific jobs, under/Library/LaunchAgentsfor jobs needed for every user, and under/Library/LaunchDaemonsto run, for example, daily maintenance tasks.
plist Skeleton
<?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.launchd.example</string> <key>Program</key> <string>/Users/Me/Desktop/push_release.sh</string> <key>RunAtLoad</key> <true/> </dict></plist>Label
Label is required for every job and should be unique.
<key>Label</key><string>com.launchd.example</string>What to Start
ProgramArgument defines the path to the program
<key>ProgramArgument</key><array> <string>mv</string> <string>/Users/Me/Desktop/push_release.sh</string></array>When to Start
RunAtLoad & <true/> starts the job as soon as the script gets loaded
<key>RunAtLoad</key><true/>StartInterval is like a timer. This will run the job every 3600 seconds, every hour, after the script is loaded. The timer stops when computer is asleep.
<key>StartInterval</key><integer>3600</integer>StartCalendarInterval lets you to specify time to run the program. The below example will run the program everyday at 11:11PM
<key>StartCalendarInterval</key><dict> <key>Hour</key> <integer>23</integer> <key>Minute</key> <integer>11</integer></dict>Available Keys:
Month(1..12),Day(1..31),Weekday(0(Sunday)..7(Sunday)),Hour(0..23),Minute(0..59)
Below example will run once an hour
<key>StartCalendarInterval</key><dict> <key>Hour</key> <integer>1</integer></dict>You can use <array> to define multiple calendar intervals
<key>StartCalendarInterval</key><array> <dict> <key>Hour</key> <integer>23</integer> <key>Minute</key> <integer>11</integer> </dict> <dict> <key>Minute</key> <integer>0</integer> <key>Weekday</key> <integer>7</integer> </dict></array>Run at 11:11PM every day and every hour on Sundays
- [Git]: Authenticate GitHub Account with SSH Key (macOS, Linux)
- [Git + cron]: Schedule your git commands on AWS EC2