Skip to content

Q: streaming output from an execute #395

Open

Description

@jrochkind

Using sshkit 1.11.1 via Capistrano 3.4.0.

I'd like to do an execute, but get streaming output to the console as it appears from the execute. For me, execute seems to have no output. capture can capture output (that you can write out), but only once it's all done, not streaming.

Looking at the sshkit README, I think I could write my own interaction_handler to do this, not because I actually want any interaction, just to capture and write to console the output as it happens.

But it seems like there's probably a built-in way to do this I'm not finding?

Activity

  1. jrochkind commented on Mar 22, 2017

    @jrochkind
    Author

    Indeed writing my own very simple interaction handler does what I want.

    class StreamOutputInteractionHandler
    
      def initialize(log_level=:info)
        @log_level = log_level
      end
    
      def on_data(_command, stream_name, data, channel)
        log(data)
      end
    
      private
    
      def log(message)
        SSHKit.config.output.send(@log_level, message) unless @log_level.nil?
      end
    end
    

    So that's awesoe that sshkit is flexible enough to accomodate it. I think this is a common use case though, I'm wondering if it's built in a way I did not notice?

  2. leehambley commented on Mar 22, 2017

    @leehambley
    Member

    Hey @jrochkind thanks for the quick spike, that's super interesting - perhaps for now, without closing the door on this topic an entry in the EXAMPLES.md would be a welcome addition?

    How to stream output of long running commands

    And, since it's a totally valid use-case, maybe we could think about renaming the interaction handler class and tightening up some of these APIs to become part of our public spec?

  3. jrochkind commented on Mar 22, 2017

    @jrochkind
    Author

    The output I actually wanted to capture was a ruby-progressbar progress bar -- it does something kind of reasonable for non-TTY output, but is still output progress bar markers char-by-char without newlines, which looked weird in the log.

    So one further refinement, I made the class so if you set :log_level to :stderr (I know, not really a log level), it uses $stderr.print data instead of log(data). (note print not puts)

    Which actually ends up working quite nicely for a ruby-progressbar non-TTY progressbar!

    I don't know if I'll do a PR anytime soon, I'm in the middle of some yak-shaving, but at least this GH issue stands as google-able docs for anyone looking for a solution (or who wants to take it further into a PR) -- I've found answers in existing GH Issues all the time!

  4. jrochkind commented on Mar 22, 2017

    @jrochkind
    Author

    I did link to this issue and example on the existing StackOverflow i found asking the same thing, that didn't yet have a satisfactory (to me anyway) answer. http://stackoverflow.com/questions/19291438/capistrano-3-x-capture-output-line-by-line/42959611

  5. jrochkind commented on Mar 22, 2017

    @jrochkind
    Author

    and thanks for the quick response and confirmation that what I'm doing makes some sense! and for sshkit/capistrano!

  6. leehambley commented on Mar 22, 2017

    @leehambley
    Member

    Thanks @jrochkind let's leave this here and see if someone comes back to it. I'll label it appropriately. Good luck with the yak shaving.

    yak shaving

  7. jrochkind commented on Mar 22, 2017

    @jrochkind
    Author

    Thanks! Fine with me if you close it, it's still here to be googled.

    Not this issue, but i'm having trouble figuring out if execute runs on all hosts in current matching set, or just the first one it finds, and if the latter if there's any way to get it to do just the first one it finds. Can't seem to find this in any docs.

  8. mattbrictson commented on Mar 23, 2017

    @mattbrictson
    Member

    i'm having trouble figuring out if execute runs on all hosts in current matching set, or just the first one it finds

    It depends on the :in option passed to on. By default, it uses in: parallel, which means that execute is run on all hosts in the matching set simultaneously. You can also use in: sequence, in which case the first host is executed, then once that finishes, the second host, and so on.

    If you'd like to limit the hosts to just a single host, that's up to you to handle before calling on. Capistrano has a way to do this using primary().

  9. emkookmer commented on Aug 6, 2018

    @emkookmer

    Hey @jrochkind thanks for the quick spike, that's super interesting - perhaps for now, without closing the door on this topic an entry in the EXAMPLES.md would be a welcome addition?

    This would be a welcome addition, as I have a similar problem with php composer

  10. jrochkind commented on Aug 6, 2018

    @jrochkind
    Author

    I'm not certain if that example still works for me, I think it may have stopped working for me since then, I have not gotten to the bottom of what changed. :(

  11. jrochkind commented on Aug 6, 2018

    @jrochkind
    Author

    I agree it would be awesome if cap could officially handle this use case somehow and document it; I don't currently have the bandwidth to help provide a solution, sorry.

  12. mperham commented on Dec 19, 2024

    @mperham

    FYI this works for me:

    class StreamHandler
      def on_data(command, stream_name, data, channel)
        puts data
      end
    end
    
    ...
    
          execute("/tmp/stage1", interaction_handler: StreamHandler.new)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions