如何使用launchd .plist文件守护grunt watch命令?

时间:2017-06-11 16:35:41

标签: macos gruntjs launchd grunt-contrib-watch launch-agent

当我的用户登录我的OS X计算机时,我正在尝试运行grunt watch,因此我不必每次都手动在$ APP_ROOT目录中运行grunt watch

我在org.grunt.watch.plist中有以下/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是我创建的shell脚本:

#!/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

该脚本应使用daemon实用程序(随brew install daemon安装)和&#34; daemonize&#34; grunt watch命令反过来加载Gruntfile.js配置并启动监视任务。

当我与用户手动运行CLI中的/usr/local/bin/grunt-watch-launchd时,一切正常。我看到daemon ps aux | grep grunt运行grunt watch命令。如果我在APP_ROOT内编辑文件,Grunt的watch任务会触发Gruntfile.js内的特定任务。

但问题是,当我与用户登录时,我希望launchd自动启动/usr/local/bin/grunt-watch-launchd。问题是当我运行launchctl

launchctl load /Library/LaunchAgents/org.grunt.watch.plist

未加载服务。或者至少,launchd调用脚本(因为我看到文件/tmp/grunt-watch-launchd.log已创建,因此脚本运行,尽管/tmp/grunt-watch-launchd.log为空),但似乎没有创建守护程序进程或以某种方式被launchd杀死。

此外,/var/log/system.log内没有任何内容。如果我尝试使用sudo运行launchctl

sudo launchctl unload /Library/LaunchAgents/org.grunt.watch.plist && sudo launchctl load /Library/LaunchAgents/org.grunt.watch.plist

/tmp/grunt-watch-launchd.log将包含以下行:

daemon: fatal: refusing to execute unsafe program: /usr/local/bin/grunt (/usr/local/lib is group writable)

使用sudo,/var/log/system.log告诉我:

Jun 11 18:22:24 antons-mbp com.apple.xpc.launchd[1] (org.grunt.watch[83009]): Service exited with abnormal code: 1

无论哪种方式(launchctl有和没有sudo),我都可以确认服务没有启动:

mymachine:~ user$ launchctl list | grep grunt
-   0   org.grunt.watch

当我的用户登录时,运行此脚本作为守护程序的正确方法是什么?

感谢您的关注。

编辑: 我忘了说我使用Mac OS X 10.12 Sierra,用npm安装了Grunt,我使用的是MacBook Pro(13英寸,2012年中)(如果这可能有帮助)。

编辑2: 我发现了这个问题。当作为launchd代理运行时,脚本无法找到命令,因为代理的用户不是我的用户。因此发生了127错误。

所以这对于像我这样坚持一个非常简单的launchd代理人的所有人来说:

  • 始终检查您使用的命令是否在启动命令的用户的PATH中。使用绝对路径,如有必要,在脚本的第一行设置PATH变量。

  • 将所有命令输出重定向到文件,以便在launchd启动脚本时查看是否发生错误(在我的情况下,错误没有显示在/var/log/system.log中出于某种原因)。

1 个答案:

答案 0 :(得分:1)

我不熟悉gruntdaemon实用程序,但我非常确定daemon与launchd管理作业的方式相冲突。从本质上讲,您使用两种不同的守护程序创建工具来执行不同的操作,而您只需选择一种。

更具体地说,我怀疑daemon正在后台启动grunt(即守护它)。但是,launchd希望它所管理的工作保持在可以监控和控制它们的前台。可能发生的事情是launchd启动你的脚本,脚本在后台运行grunt watch然后退出...并且launchd看到作业已经退出并帮助清理剩余的子进程,如{{1} }。结果:没有实际的grunt守护程序正在运行。

您可以将grunt添加到.plist中,告诉launchd不要杀掉后台进程,但实际上最好以启动方式执行操作并将<key>AbandonProcessGroup</key><true/>留在前台。因此,要么从您的脚本中删除grunt命令,要么将其替换为daemon,它将作为launchd下的顶级进程运行exec(正常运行它会使其成为shell的子进程,并将shell保留为launchd下的顶级进程。

这里可能还有其他问题,但在修复此问题之前很难说清楚。