Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change log

## [Unreleased]

### Added
* Add :tag option to add custom tags to command output

## [v0.10.1] - 2021-02-14

### Fixed
Expand Down
27 changes: 23 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ Or install it yourself as:
* [2.3. Logging](#23-logging)
* [2.3.1. Color](#231-color)
* [2.3.2. UUID](#232-uuid)
* [2.3.3. Only output on error](#233-only-output-on-error)
* [2.3.4. Verbose](#234-verbose)
* [2.3.3. Tag](#233-tag)
* [2.3.4. Only output on error](#234-only-output-on-error)
* [2.3.5. Verbose](#235-verbose)
* [2.4. Dry run](#24-dry-run)
* [2.5. Wait](#25-wait)
* [2.6. Test](#26-test)
Expand Down Expand Up @@ -221,7 +222,25 @@ cmd.run("echo hello", uuid: false)
# Finished in 0.003 seconds with exit status 0 (successful)
```

#### 2.3.3 Only output on error
#### 2.3.3 Tag

You can add custom tags to command output using the `:tag` option. This is useful for categorizing or identifying a specific instance or run of a command:

```ruby
cmd = TTY::Command.new(tag: "deploy")
cmd.run("echo hello")

# or individually per command run:
cmd = TTY::Command.new
cmd.run("echo hello", tag: "deploy")

# =>
# [deploy] Running echo hello
# hello
# [deploy] Finished in 0.003 seconds with exit status 0 (successful)
```

#### 2.3.4 Only output on error

When using a command that can fail, setting `:only_output_on_error` option to `true` hides the output if the command succeeds:

Expand Down Expand Up @@ -254,7 +273,7 @@ will also print the output.

*Setting this option will cause the output to show at once, at the end of the command.*

#### 2.3.4 Verbose
#### 2.3.5 Verbose

By default commands will produce warnings when, for example `pty` option is not supported on a given platform. You can switch off such warnings with `:verbose` option set to `false`.

Expand Down
6 changes: 5 additions & 1 deletion lib/tty/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,18 @@ def initialize(**options)
@output = options.fetch(:output) { $stdout }
@color = options.fetch(:color) { true }
@uuid = options.fetch(:uuid) { true }
@tag = options[:tag]
@printer_name = options.fetch(:printer) { :pretty }
@dry_run = options.fetch(:dry_run) { false }
@printer = use_printer(@printer_name, color: @color, uuid: @uuid)
@printer = use_printer(
@printer_name, color: @color, uuid: @uuid, tag: @tag
)
@cmd_options = {}
@cmd_options[:verbose] = options.fetch(:verbose, true)
@cmd_options[:pty] = true if options[:pty]
@cmd_options[:binmode] = true if options[:binmode]
@cmd_options[:timeout] = options[:timeout] if options[:timeout]
@cmd_options[:tag] = @tag if @tag
end

# Start external executable in a child process
Expand Down
9 changes: 8 additions & 1 deletion lib/tty/command/cmd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ def group(value)
def with_clean_env
end

# The command tag
# @api public
def tag
@options[:tag]
end

# Assemble full command
#
# @api public
Expand All @@ -140,7 +146,8 @@ def to_hash
{
command: command,
argv: argv,
uuid: uuid
uuid: uuid,
tag: @options[:tag]
}
end

Expand Down
7 changes: 4 additions & 3 deletions lib/tty/command/printers/pretty.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Pretty < Abstract
def initialize(*)
super
@uuid = options.fetch(:uuid, true)
@tag = options[:tag]
end

def print_command_start(cmd, *args)
Expand Down Expand Up @@ -46,10 +47,10 @@ def print_command_exit(cmd, status, runtime, *args)
def write(cmd, message, data = nil)
cmd_set_uuid = cmd.options.fetch(:uuid, true)
uuid_needed = cmd.options[:uuid].nil? ? @uuid : cmd_set_uuid
prefix = cmd.tag || @tag || (uuid_needed ? cmd.uuid : nil)

out = []
if uuid_needed
out << "[#{decorate(cmd.uuid, :green)}] " unless cmd.uuid.nil?
end
out << "[#{decorate(prefix, :green)}] " if prefix
out << "#{message}\n"
target = (cmd.only_output_on_error && !data.nil?) ? data : output
target << out.join
Expand Down
167 changes: 111 additions & 56 deletions spec/unit/run_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,14 @@
end

it "runs command successfully with logging" do
output = StringIO.new
uuid = "xxxx"
allow(SecureRandom).to receive(:uuid).and_return(uuid)
command = TTY::Command.new(output: output)

command.run(:echo, "hello")
lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output)
command.run(:echo, "hello")
end

output.rewind
lines = output.readlines
lines.last.gsub!(/\d+\.\d+/, "x")
expect(lines).to eq([
"[\e[32m#{uuid}\e[0m] Running \e[33;1mecho hello\e[0m\n",
"[\e[32m#{uuid}\e[0m] \thello\n",
Expand All @@ -31,67 +29,31 @@
end

it "runs command successfully with logging without color" do
output = StringIO.new
uuid = "xxxx"
allow(SecureRandom).to receive(:uuid).and_return(uuid)
command = TTY::Command.new(output: output, color: false)

command.run(:echo, "hello")
lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output, color: false)
command.run(:echo, "hello")
end

output.rewind
lines = output.readlines
lines.last.gsub!(/\d+\.\d+/, "x")
expect(lines).to eq([
"[#{uuid}] Running echo hello\n",
"[#{uuid}] \thello\n",
"[#{uuid}] Finished in x seconds with exit status 0 (successful)\n"
])
end

it "runs command successfully with logging without uuid set globally" do
output = StringIO.new
command = TTY::Command.new(output: output, uuid: false)

command.run(:echo, "hello")
output.rewind

lines = output.readlines
lines.last.gsub!(/\d+\.\d+/, "x")
expect(lines).to eq([
"Running \e[33;1mecho hello\e[0m\n",
"\thello\n",
"Finished in x seconds with exit status 0 (\e[32;1msuccessful\e[0m)\n"
])
end

it "runs command successfully with logging without uuid set locally" do
output = StringIO.new
command = TTY::Command.new(output: output)

command.run(:echo, "hello", uuid: false)
output.rewind

lines = output.readlines
lines.last.gsub!(/\d+\.\d+/, "x")
expect(lines).to eq([
"Running \e[33;1mecho hello\e[0m\n",
"\thello\n",
"Finished in x seconds with exit status 0 (\e[32;1msuccessful\e[0m)\n"
])
end

it "runs command and fails with logging" do
non_zero_exit = fixtures_path("non_zero_exit")
output = StringIO.new
uuid = "xxxx"
allow(SecureRandom).to receive(:uuid).and_return(uuid)
command = TTY::Command.new(output: output)

command.run!("ruby #{non_zero_exit}")
lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output)
command.run!("ruby #{non_zero_exit}")
end

output.rewind
lines = output.readlines
lines.last.gsub!(/\d+\.\d+/, "x")
expect(lines).to eq([
"[\e[32m#{uuid}\e[0m] Running \e[33;1mruby #{non_zero_exit}\e[0m\n",
"[\e[32m#{uuid}\e[0m] \tnooo\n",
Expand Down Expand Up @@ -145,17 +107,16 @@
phased_output = fixtures_path("phased_output")
uuid = "xxxx"
allow(SecureRandom).to receive(:uuid).and_return(uuid)
output = StringIO.new
cmd = TTY::Command.new(output: output)

out, err = cmd.run("ruby #{phased_output}")
out = err = nil
lines = retrieve_log_lines do |output|
cmd = TTY::Command.new(output: output)
out, err = cmd.run("ruby #{phased_output}")
end

expect(out).to eq("." * 10)
expect(err).to eq("")

output.rewind
lines = output.readlines
lines.last.gsub!(/\d+\.\d+/, "x")
expect(lines).to eq([
"[\e[32m#{uuid}\e[0m] Running \e[33;1mruby #{phased_output}\e[0m\n",
"[\e[32m#{uuid}\e[0m] \t..........\n",
Expand Down Expand Up @@ -184,4 +145,98 @@
lines = output.readlines
expect(lines[0]).to include("Running \e[33;1mecho hello\e[0m\n")
end

context "with uuid option" do
it "runs command successfully with logging without uuid set globally" do
lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output, uuid: false)
command.run(:echo, "hello")
end

expect(lines).to eq(
generic_colored_log_lines(prefix: nil)
)
end

it "runs command successfully with logging without uuid set locally" do
lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output)
command.run(:echo, "hello", uuid: false)
end

expect(lines).to eq(
generic_colored_log_lines(prefix: nil)
)
end
end

context "with tag option" do
it "prints the tag set globally" do
tag = "task"

lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output, tag: tag)
command.run(:echo, "hello")
end

expect(lines).to eq(
generic_colored_log_lines(prefix: tag)
)
end

it "prints the tag set locally" do
tag = "task"

lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output)
command.run(:echo, "hello", tag: tag)
end

expect(lines).to eq(
generic_colored_log_lines(prefix: tag)
)
end

it "prints the tag even if uuid is set to false" do
tag = "task"

lines = retrieve_log_lines do |output|
command = TTY::Command.new(output: output, tag: tag, uuid: false)
command.run(:echo, "hello")
end

expect(lines).to eq(
generic_colored_log_lines(prefix: tag)
)
end
end

# Retrieves log lines from the output produced within the given block.
# Also replaces the execution time portion in the output with `x`.
def retrieve_log_lines
output = StringIO.new
yield(output)
output.rewind
lines = output.readlines
lines.last.gsub!(/\d+\.\d+/, "x")
lines
end

# Generates the expected log lines in colored mode, with/without `[prefix]`
def generic_colored_log_lines(prefix: nil)
if prefix
[
"[\e[32m#{prefix}\e[0m] Running \e[33;1mecho hello\e[0m\n",
"[\e[32m#{prefix}\e[0m] \thello\n",
"[\e[32m#{prefix}\e[0m] Finished in x seconds with exit status 0 " \
"(\e[32;1msuccessful\e[0m)\n"
]
else
[
"Running \e[33;1mecho hello\e[0m\n",
"\thello\n",
"Finished in x seconds with exit status 0 (\e[32;1msuccessful\e[0m)\n"
]
end
end
end