防止 Rake 的 sh 命令回显命令

Prevent Rake's sh command from echoing the command

每当我从 rake 调用 sh 时,它通常会在 运行 之前回显 运行 的命令。如何防止 sh 将命令记录到标准输出。我想阻止这种情况,因为我在调用的命令中有 api 键,我不想在我的构建日志中公开它们。

解决这个问题有两个部分。第一种是传递 verbose: false 选项,这将防止命令在执行之前被打印出来:

$ cat Rakefile
SECRET = 'foobarbaz'

task :foo do
  sh "echo #{SECRET} > secrets.txt", verbose: false
end

$ rake foo
(no output)

但是,如果出现错误,这将无济于事,因为如果 returns 出现错误,Rake 将打印失败的命令:

$ cat Rakefile
SECRET = 'foobarbaz'

task :foo do
  sh "echo #{SECRET} > secrets.txt; exit 1", verbose: false
end

$ rake foo
rake aborted!
Command failed with status (1): [echo foobarbaz > secrets.txt; exit 1...]
...

解决方法在in the docs for sh:

If a block is given, upon command completion the block is called with an OK flag (true on a zero exit status) and a Process::Status object. Without a block a RuntimeError is raised when the command exits non-zero.

您可以看到默认行为的来源 in the Rake source。那么,解决方案是提供我们自己的块:

$ cat Rakefile
SECRET = "foobarbaz"

task :foo do
  sh "echo #{SECRET} > secrets.txt; exit 1", verbose: false do |ok, status|
    unless ok
      fail "Command failed with status (#{status.exitstatus}): [command hidden]"
    end
  end
end

$ rake foo
rake aborted!
Command failed with status (1): [command hidden]
...

看起来不错!

如果你发现自己在多个地方需要这个,你可以写一个方便的方法;像这样:

def quiet_sh(*cmd)
  options = (Hash === cmd.last) ? cmd.pop : {}
  options = { verbose: false }.merge(options)

  sh *cmd, options do |ok, status|
    unless ok
      fail "Command failed with status (#{status.exitstatus}): [command hidden]"
    end
  end
end

SECRET = "foobarbaz"

task :foo do
  quiet_sh "do_secret_things"
end