[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

BASH
cd ~/Desktop/project
git add .
git commit -m "[RELEASE]: 1.0.1"
git push

If you want to create a log file as a result, you can put output to file

BASH
cd ~/Desktop/project
git add .
git commit -m "[RELEASE]: 1.0.1" > /tmp/commit_log 2>&1
git push > /tmp/push_log 2>&1

2&>1 indicates 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
<?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.sh on 3/22 at 11:11AM

Please read More about launchd to learn more details

Load launchd

You need to load the plist file after it's created.

BASH
launchctl load com.launchd.example.plist

To verify that it's loaded

BASH{OUTPUTLINES:
launchctl list | grep com.launchd.example
- 0 com.lanchd.example

Receiving output, i.e. - 0 com.launchd.example means that the job is currently loaded

-: means that it is loaded but is not running

0 means job finished successfully. Any positive number means that there was an error, and negative number denotes a termination

To unload the job

BASH
launchctl unload com.launchd.example.plist

Be 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

TypeLocationWho Runs
User Agents*~/Library/LaunchAgentsCurrently 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/LaunchAgents for user specific jobs, under /Library/LaunchAgents for jobs needed for every user, and under/Library/LaunchDaemons to run, for example, daily maintenance tasks.

plist Skeleton

XML
<?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.

XML
<key>Label</key>
<string>com.launchd.example</string>

What to Start

ProgramArgument defines the path to the program

XML
<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

XML
<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.

XML
<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

XML
<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

XML
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>1</integer>
</dict>

You can use <array> to define multiple calendar intervals

XML
<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


WRITTEN BY

Keeping a record