I am trying to run grunt watch
when my user logs into my OS X machine, so that I don't have to run grunt watch
in my $APP_ROOT dir manually every time.
I have the following org.grunt.watch.plist
file within /Library/LaunchAgents
:
<?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>org.grunt.watch</string>
<key>RunAtLoad</key>
<true/>
<key>ProgramArguments</key>
<array>
<string>/usr/local/bin/grunt-watch-launchd</string>
</array>
</dict>
</plist>
/usr/local/bin/grunt-watch-launchd
is a shell script I created:
#!/bin/sh
APP_ROOT=/path/to/htdocs/app
/usr/local/bin/daemon -- /usr/local/bin/grunt watch --gruntfile="$APP_ROOT"/Gruntfile.js > /tmp/grunt-watch-launchd.log 2>&1
The script should use the daemon
utility (installed with brew install daemon
) and "daemonize" the grunt watch
command which in turn loads a Gruntfile.js
configuration and starts the watch task.
When I manually run /usr/local/bin/grunt-watch-launchd
from the CLI with my user, everything works great. I see a daemon
with ps aux | grep grunt
which runs the grunt watch
command. If I then edit a file inside APP_ROOT
, Grunt's watch
task triggers the specific tasks inside my Gruntfile.js
.
But the point is that I would like that launchd
automatically starts /usr/local/bin/grunt-watch-launchd
when I log in with my user. The problem is that when I run launchctl
:
launchctl load /Library/LaunchAgents/org.grunt.watch.plist
The service is not loaded. Or at least, launchd
calls the script (because I see that the file /tmp/grunt-watch-launchd.log
is created so the script runs, though /tmp/grunt-watch-launchd.log
is empty), but the daemon process seems not to be created or is somehow killed by launchd
.
Also, nothing appears inside /var/log/system.log
. If I try to run launchctl
with sudo:
sudo launchctl unload /Library/LaunchAgents/org.grunt.watch.plist && sudo launchctl load /Library/LaunchAgents/org.grunt.watch.plist
/tmp/grunt-watch-launchd.log
will contain the following line:
daemon: fatal: refusing to execute unsafe program: /usr/local/bin/grunt (/usr/local/lib is group writable)
Using sudo, /var/log/system.log
tells me:
Jun 11 18:22:24 antons-mbp com.apple.xpc.launchd[1] (org.grunt.watch[83009]): Service exited with abnormal code: 1
In either ways (launchctl
with and without sudo), I can confirm that the service is not started:
mymachine:~ user$ launchctl list | grep grunt
- 0 org.grunt.watch
What's wrong and what's the correct way to run this script as a daemon when my user logs in?
Thanks for your attention.
EDIT:
I forgot to say that I use Mac OS X 10.12 Sierra, installed Grunt with npm
and I am using a MacBook Pro (13-inch, Mid 2012) (if that may help).
EDIT 2:
I found the issue. When running as a launchd
agent, the script was not able to find a command because the user of the agent wasn't my user. Therefore a 127 error occurred.
So this is for all the guys like me that stuck on a really simple launchd
agent:
Always check that the commands you use are in the PATH of the user who launches the command. Use absolute paths, and if necessary, set the PATH variable on the first line of your script.
Redirect all the command output to a file so that you can see if an error occurs when
launchd
launches the script (in my case the error didn't appear in/var/log/system.log
for some reason).