File: cmd.rb

package info (click to toggle)
ruby-tty-command 0.10.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 452 kB
  • sloc: ruby: 1,990; makefile: 4; sh: 4
file content (157 lines) | stat: -rw-r--r-- 3,715 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# frozen_string_literal: true

require "securerandom"
require "shellwords"

module TTY
  class Command
    # Encapsulates the executed command
    #
    # @api private
    class Cmd
      # A string command name, or shell program
      # @api public
      attr_reader :command

      # A string arguments
      # @api public
      attr_reader :argv

      # Hash of operations to peform
      # @api public
      attr_reader :options

      # Unique identifier
      # @api public
      attr_reader :uuid

      # Flag that controls whether to print the output only on error or not
      attr_reader :only_output_on_error

      # Initialize a new Cmd object
      #
      # @api private
      def initialize(env_or_cmd, *args)
        opts = args.last.respond_to?(:to_hash) ? args.pop : {}
        if env_or_cmd.respond_to?(:to_hash)
          @env = env_or_cmd
          unless command = args.shift
            raise ArgumentError, "Cmd requires command argument"
          end
        else
          command = env_or_cmd
        end

        if args.empty? && cmd = command.to_s
          raise ArgumentError, "No command provided" if cmd.empty?

          @command = sanitize(cmd)
          @argv = []
        else
          if command.respond_to?(:to_ary)
            @command = sanitize(command[0])
            args.unshift(*command[1..-1])
          else
            @command = sanitize(command)
          end
          @argv = args.map { |i| Shellwords.escape(i) }
        end
        @env ||= {}
        @options = opts

        @uuid = SecureRandom.uuid.split("-")[0]
        @only_output_on_error = opts.fetch(:only_output_on_error) { false }
        freeze
      end

      # Extend command options if keys don't already exist
      #
      # @api public
      def update(options)
        @options.update(options.merge(@options))
      end

      # The shell environment variables
      #
      # @api public
      def environment
        @env.merge(options.fetch(:env, {}))
      end

      def environment_string
        environment.map do |key, val|
          converted_key = key.is_a?(Symbol) ? key.to_s.upcase : key.to_s
          escaped_val = val.to_s.gsub(/"/, '\"')
          %(#{converted_key}="#{escaped_val}")
        end.join(" ")
      end

      def evars(value, &block)
        return (value || block) unless environment.any?

        "( export #{environment_string} ; #{value || block.call} )"
      end

      def umask(value)
        return value unless options[:umask]

        %(umask #{options[:umask]} && %s) % [value]
      end

      def chdir(value)
        return value unless options[:chdir]

        %(cd #{Shellwords.escape(options[:chdir])} && #{value})
      end

      def user(value)
        return value unless options[:user]

        vars = environment.any? ? "#{environment_string} " : ""
        %(sudo -u #{options[:user]} #{vars}-- sh -c '%s') % [value]
      end

      def group(value)
        return value unless options[:group]

        %(sg #{options[:group]} -c \\\"%s\\\") % [value]
      end

      # Clear environment variables except specified by env
      #
      # @api public
      def with_clean_env
      end

      # Assemble full command
      #
      # @api public
      def to_command
        chdir(umask(evars(user(group(to_s)))))
      end

      # @api public
      def to_s
        [command.to_s, *Array(argv)].join(" ")
      end

      # @api public
      def to_hash
        {
          command: command,
          argv: argv,
          uuid: uuid
        }
      end

      private

      # Coerce to string
      #
      # @api private
      def sanitize(value)
        value.to_s.dup
      end
    end # Cmd
  end # Command
end # TTY