Ruby/Thor exit status in case of an error
Asked Answered
K

3

10

I'm new to Thor (and to Ruby) and I'm considering using it within a build script, as it's said it can be a replacement to Rake (thus to Make). However after a short trial, I'm confused about the error status it returns. I quickly went through the wiki but haven't seen any mention about that.

With just the first "Simple Example", test.thor:

class Test < Thor
  desc "example", "an example task"
  def example
    puts "I'm a thor task!"
  end
end

version #:

eruve>thor version
Thor 0.18.1

I tried the following, an erroneous command on purpose:

eruve>ruby --version; thor test:example badarg; echo exit status: $?

ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-darwin10.8.0]
ERROR: thor example was called with arguments ["badarg"]
Usage: "thor test:example".
exit status: 0

So, there was an error but it exits with status 0 nonetheless... meaning I'd rather not use it in a (non-ruby) script, or else the script would continue running even though it should terminate. Subsequent errors might be difficult to analyse.

I must be missing something, hence my questions:

  • Is there an easy way to get a non-zero status by default in case of an error (config file, etc.)?

  • If not, what am I supposed to do to get it right?

Thank you.

Kisangani answered 21/6, 2013 at 18:24 Comment(0)
K
3

Based on bundler's solution (many thanks @fontno), and more investigation on my side, here is a hack in order to have it working with a normal shell. WARNING: it's not elegant, prints out the exception stack crap, but I think that works (please don't hesitate to tell me otherwise).

class Thorough < Thor
  ENV["THOR_DEBUG"] = "1"
  check_unknown_options!
private
  def subcommand(*_) super subcommand(*_)
  rescue Thor::Error => e
    $stderr.puts e.message
    exit 1
  end
end

class Test < Thor#ough
  desc "example", "an example task"
  def example
    puts "I'm a thor task!"
  end
end

Written as above, it has the same behavior as before (I believe). Now, after removing the # from Thor#ough, it should exit with status 1 if Thor has raised an Error, thus allowing some control from e.g. a non-ruby shell.

eruve>thor test:example badarg; echo $?
/Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:482:in `handle_argument_error': ERROR: thor example was called with arguments ["badarg"] (Thor::InvocationError)
Usage: "thor test:example".
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:35:in `rescue in run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:21:in `run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/invocation.rb:120:in `invoke_command'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor.rb:363:in `dispatch'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:439:in `start'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/runner.rb:36:in `method_missing'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:29:in `run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:128:in `run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/invocation.rb:120:in `invoke_command'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor.rb:363:in `dispatch'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:439:in `start'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/bin/thor:6:in `<top (required)>'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/thor:23:in `load'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/thor:23:in `<main>'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/ruby_noexec_wrapper:14:in `eval'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/ruby_noexec_wrapper:14:in `<main>'
1

eruve>thor test:example; echo $?
I'm a thor task!
0

eruve>thor test:example badarg 2>/dev/null; echo $?
1

Cheers. PS: I wonder, are there many gotchas like this in Thor? If it's an expected behavior, its purpose/philosophy is incompatible with the needs of my project... hacks are not a reliable solution.

Kisangani answered 22/6, 2013 at 5:28 Comment(0)
V
24

I know this has been answered already but I think this is a better answer so I thought I'd contribute it anyway.

Thor has a method you can use to change the behavior so errors cause non-zero exit codes. It's not documented very well (IMHO).

class Test < Thor
  def self.exit_on_failure?
    true
  end

  desc "example", "an example task"
  def example
    puts "I'm a thor task!"
  end
end

The default for this is inexplicably false. I don't know why anyone would want it to behave like that personally. Thor issue 244 addresses this as well.

Update: As of Thor 1.0.0, you will get a deprecation warning if you do not provide your own exit_on_failure? method. This was to deal with the confusing default behavior.

Vaginismus answered 28/8, 2013 at 13:57 Comment(0)
K
3

Based on bundler's solution (many thanks @fontno), and more investigation on my side, here is a hack in order to have it working with a normal shell. WARNING: it's not elegant, prints out the exception stack crap, but I think that works (please don't hesitate to tell me otherwise).

class Thorough < Thor
  ENV["THOR_DEBUG"] = "1"
  check_unknown_options!
private
  def subcommand(*_) super subcommand(*_)
  rescue Thor::Error => e
    $stderr.puts e.message
    exit 1
  end
end

class Test < Thor#ough
  desc "example", "an example task"
  def example
    puts "I'm a thor task!"
  end
end

Written as above, it has the same behavior as before (I believe). Now, after removing the # from Thor#ough, it should exit with status 1 if Thor has raised an Error, thus allowing some control from e.g. a non-ruby shell.

eruve>thor test:example badarg; echo $?
/Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:482:in `handle_argument_error': ERROR: thor example was called with arguments ["badarg"] (Thor::InvocationError)
Usage: "thor test:example".
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:35:in `rescue in run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:21:in `run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/invocation.rb:120:in `invoke_command'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor.rb:363:in `dispatch'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:439:in `start'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/runner.rb:36:in `method_missing'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:29:in `run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/command.rb:128:in `run'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/invocation.rb:120:in `invoke_command'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor.rb:363:in `dispatch'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/lib/thor/base.rb:439:in `start'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/gems/thor-0.18.1/bin/thor:6:in `<top (required)>'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/thor:23:in `load'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/thor:23:in `<main>'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/ruby_noexec_wrapper:14:in `eval'
    from /Users/eruve/.rvm/gems/ruby-2.0.0-p195/bin/ruby_noexec_wrapper:14:in `<main>'
1

eruve>thor test:example; echo $?
I'm a thor task!
0

eruve>thor test:example badarg 2>/dev/null; echo $?
1

Cheers. PS: I wonder, are there many gotchas like this in Thor? If it's an expected behavior, its purpose/philosophy is incompatible with the needs of my project... hacks are not a reliable solution.

Kisangani answered 22/6, 2013 at 5:28 Comment(0)
S
1

Good question. I also noticed this when looking a thor to use for a project. As far as I can tell this is expected behavior. This pull request for bundler has an interesting solution that may be appropriate for you.

They enabled the thor debug flag so they could catch the error and set the appropriate exit status

# bin/bundle

Bundler.with_friendly_errors {
    # Set debug flag so we can rescue Thor::error's
    # and set the correct exit code.
    ENV["THOR_DEBUG"] = "1"
    Bundler::CLI.start
}


# friendly_errors

rescue Thor::UndefinedCommandError => e
    Bundler.ui.error e.message
    exit 15
  rescue Thor::Error => e
    Bundler.ui.error e.message
    exit 1
Sternmost answered 21/6, 2013 at 20:34 Comment(1)
+1 because it led to a hack (see my answer), although that's not directly usable, in particular as a solution outside of Ruby.Kisangani

© 2022 - 2024 — McMap. All rights reserved.