[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