1

I'm using the sass-lint NPM package to style-check .scss files from within a Rake task, thus:

  sass_lint_cmd = "sass-lint --config #{ui_library_path}/scss/.sass-lint.yml '#{ui_library_path}/scss/*.scss' -v -q --max-warnings=0"
  output, status = Open3.capture2e(sass_lint_cmd)
  raise IOError, output unless status == 0

This basically works, insofar as in the event of any linter warnings or errors the Rake task aborts and the sass-lint output, including errors, is dumped to the console.

However, when run directly, sass-lint produces nice colorized output. When captured by capture2e, the colors are lost.

I assume the issue is that sass-lint (or Node) detects it's not running in a TTY, and so outputs plain text. Is there some Process.spawn() option I can pass to Open3.capture2e(), or some other method, by which I can make it think it's running in a TTY?

(Note: I did look at Trick an application into thinking its stdout is a terminal, not a pipe, but the BSD version of script that ships with macOS doesn't seem to support either the --return or the -c options, and I'm running on macOS.)


Update: I tried script -q /dev/null and PTY.spawn() as per Piccolo's answer, but no luck.

script -q /dev/null … works from the command line, but doesn't work in Open3.capture2e() (it runs, but produces monochrome output and a spurious Bundler::GemNotFound stack trace).

As for PTY.spawn(), replacing the code above with the following:

r, _w, pid = PTY.spawn(scss_lint_command)
_, proc_status = Process.wait2(pid)
output, status = [r, proc_status.exitstatus]
(warn(output); raise) unless status == 0

the subprocess never seems to complete; if I ps in another terminal it shows as in interruptible sleep status. Killing the subprocess doesn't free up the parent process.

The same happens with the block form.

output, status = nil
PTY.spawn(scss_lint_command) do |r, _w, pid|
  _, proc_status = Process.wait2(pid)
  output, status = [r, proc_status.exitstatus]
end
(warn(output); raise) unless status == 0
David Moles
  • 48,006
  • 27
  • 136
  • 235

1 Answers1

1

Have you considered using Ruby's excellent pty library instead of Open3?

Pseudo terminals, per the thread you linked, seem to emulate an actual TTY, so the script wouldn't know it wasn't in a terminal unless it checked for things like $TERM, but that can also be spoofed with relative ease.

According to this flowchart, the downside of using pty instead of Open3 is that STDERR does not get its own stream.


Alternatively, per this answer, also from the thread you linked, script -q /dev/null $COMMAND appears to do the trick on Mac OS X.

On Macs, ls -G colorizes the output of ls, and as a brief test, I piped ls -G into cat as follows:

script -q /dev/null ls -G | cat

and it displayed with colors, whereas simply running

ls -G | cat

did not.

This method also worked in irb, again using ls -G:

$ touch regular_file
$ touch executable_file
$ mkdir directory
$ chmod +x executable_file
$ irb
2.4.1 :001 > require 'Open3'
 => true
2.4.1 :002 > output, status = Open3.capture2e("ls -G")
 => ["directory\nexecutable_file\nregular_file\n", #<Process::Status: pid 39299 exit 0>]
2.4.1 :003 > output, status = Open3.capture2e("script -q /dev/null ls -G")
 => ["^D\b\b\e[1m\e[36mdirectory\e[39;49m\e[0m       \e[31mexecutable_file\e[39;49m\e[0m regular_file\r\n", #<Process::Status: pid 39301 exit 0>]
2.4.1 :004 >
Piccolo
  • 1,612
  • 4
  • 22
  • 38
  • Tried both, no luck. :( See update. But I'm probably not calling `PTY.spawn()` / `Process.wait2()` correctly. – David Moles Jul 25 '18 at 22:52
  • @DavidMoles Hmm, not sure about the `script -q /dev/null ...` not working, but let me see if I can figure out what's wrong with your `PTY.spawn()`ing. – Piccolo Jul 25 '18 at 23:01
  • @DavidMoles I tried running the following: `lines = []; PTY.spawn("ls -G && sleep 5") {|reader, writer, pid| reader.each {|line| lines << line }; Process.wait2(pid) }`, and `Process.wait2(pid)` blocked for the right amount of time, and the output was correct. I don't really see any difference between what you did and that; can you try running mine on your system so we can see if it's a difference between `ls -G` and your command, or if it's a system issue? – Piccolo Jul 25 '18 at 23:20