Class: Pmgmt

Inherits:
Object
  • Object
show all
Defined in:
lib/pmgmt.rb

Constant Summary collapse

@@commands =

State to hold registered commands. See register_command.

[]
@@options_parser =

Convenience method to provide access to the options parsing utility class.

PmgmtLib::OptionsParser

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePmgmt

Returns a new instance of Pmgmt.



119
120
121
122
# File 'lib/pmgmt.rb', line 119

def initialize()
  @docker = PmgmtLib::DockerHelper.new(self)
  @sf = PmgmtLib::SyncFiles.new(self)
end

Instance Attribute Details

#dockerObject (readonly)

Convenience method to easily retrieve Docker utilities. See PmgmtLib::DockerHelper.



115
116
117
# File 'lib/pmgmt.rb', line 115

def docker
  @docker
end

#sfObject (readonly)

Convenience method to easily retrieve file-syncing utilities. See PmgmtLib::SyncFiles.



117
118
119
# File 'lib/pmgmt.rb', line 117

def sf
  @sf
end

Class Method Details

.commandsArray

Returns the commands array for inspection.

Returns:

  • (Array)

    the commands array for inspection.



72
73
74
# File 'lib/pmgmt.rb', line 72

def self.commands()
  @@commands
end

.handle_or_die(args) ⇒ Object

Handles command execution.

Parameters:

  • args (Array)

    Pass ARGV.



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
# File 'lib/pmgmt.rb', line 78

def self.handle_or_die(args)
  if args.length == 0 or args[0] == "--help"
    self.new.print_usage
    exit 0
  end

  if args[0] == "--cmplt" # Shell completion argument name inspired by vault
    # Form of args: --cmplt <index-of-current-argument> ./project.rb arg arg arg
    index = args[1].to_i
    word = args[2 + index]
    puts @@commands.select{ |x| x[:invocation].start_with?(word) }
        .map{ |x| x[:invocation]}.join("\n")
    exit 0
  end

  command = args.first
  handler = @@commands.select{ |x| x[:invocation] == command }.first
  if handler.nil?
    self.new.error "#{command} command not found."
    exit 1
  end

  fn = handler[:fn]
  args = args
  if fn.is_a?(Symbol)
    fn = method(fn)
  else
    args = args.drop(1)
  end
  if fn.arity == 0
    fn.call()
  else
    fn.call(*args)
  end
end

.load_scripts(scripts_dir) ⇒ void

This method returns an undefined value.

Loads all ‘.rb` scripts from the given directory. Scripts are expected to register commands as a side-effect of loading.

Parameters:

  • scripts_dir (String)


23
24
25
26
27
28
29
30
31
32
33
# File 'lib/pmgmt.rb', line 23

def self.load_scripts(scripts_dir)
  if !File.directory?(scripts_dir)
    self.new.error "Cannot load scripts. Not a directory: #{scripts_dir}"
    exit 1
  end
  Dir.foreach(scripts_dir) do |item|
    if item =~ /[.]rb$/
      require "#{scripts_dir}/#{item}"
    end
  end
end

.OptionsParserObject



15
16
17
# File 'lib/pmgmt.rb', line 15

def self.OptionsParser
  @@options_parser
end

.register_command(command) ⇒ void

This method returns an undefined value.

Registers a command. Registered commands can be invoked by name, appear in command lists and allow –help docs.

Parameters:

  • command (Map)

    The :invocation key is what would be typed to invoke the command and the :fn key is what function to execute when invoked. Specify only a :fn key for shorthand.



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
# File 'lib/pmgmt.rb', line 39

def self.register_command(command)
  if command.nil?
    self.new.error "register_command called with nil argument"
    exit 1
  end
  if command.is_a?(Symbol)
    invocation = command.to_s
    fn = command
  else
    invocation = command[:invocation]
    fn = command[:fn]
  end
  if fn.nil?
    self.new.error "No :fn key defined for command #{invocation}"
    exit 1
  end
  if fn.is_a?(Symbol)
    unless Object.private_method_defined?(fn)
      self.new.error "Function #{fn.to_s} is not defined for #{invocation}."
      exit 1
    end
  else
    # TODO(dmohs): Deprecation warning.
    unless fn.is_a?(Proc)
      self.new.error ":fn key for #{invocation} does not define a Proc or Symbol"
      exit 1
    end
  end

  @@commands.push({:invocation => invocation, :fn => fn})
end

Instance Method Details

#blue_term_text(text) ⇒ Object



150
151
152
# File 'lib/pmgmt.rb', line 150

def blue_term_text(text)
  "\033[0;36m#{text}\033[0m"
end

#bold_term_text(text) ⇒ Object



158
159
160
# File 'lib/pmgmt.rb', line 158

def bold_term_text(text)
  "\033[1m#{text}\033[0m"
end

#capture_stdout(cmd, err: STDERR) ⇒ String

Runs the given command and captures its stdout.

Parameters:

  • cmd (Array)

    Command to run.

  • err: (defaults to: STDERR)

    Pass nil to suppress stderr output.

Returns:

  • (String)

    Captured stdout.



197
198
199
200
201
202
203
# File 'lib/pmgmt.rb', line 197

def capture_stdout(cmd, err: STDERR)
  if err.nil?
    err = "/dev/null"
  end
  output, _ = Open3.capture2(*cmd, :err => err)
  output
end

#error(text) ⇒ void

This method returns an undefined value.

Prints “error” text (red) on STDERR.



176
177
178
# File 'lib/pmgmt.rb', line 176

def error(text)
  STDERR.puts red_term_text(text)
end

#load_envObject



138
139
140
141
142
143
144
# File 'lib/pmgmt.rb', line 138

def load_env()
  if not File.exists?("project.yaml")
    error "Missing project.yaml"
    exit 1
  end
  OpenStruct.new YAML.load(File.read("project.yaml"))
end

#pipe(*cmds) ⇒ Object

Accepts multiple arrays of commands and pipes each one to the next, failing if any command exits with an error.



262
263
264
265
266
267
268
269
270
271
272
# File 'lib/pmgmt.rb', line 262

def pipe(*cmds)
  s = cmds.map { |x| x.join(" ") }
  s = s.join(" | ")
  STDERR.puts "+ #{s}"
  Open3.pipeline(*cmds).each do |status|
    unless status.success?
      error "Piped command failed"
      exit 1
    end
  end
end


124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/pmgmt.rb', line 124

def print_usage()
  STDERR.puts "\nUsage: #{$PROGRAM_NAME} <command> <options>\n\n"
  if !@@commands.empty?
    STDERR.puts "COMMANDS\n\n"
    @@commands.each do |command|
      STDERR.puts bold_term_text(command[:invocation])
      STDERR.puts command[:description] || "[No description provided.]"
      STDERR.puts
    end
  else
    STDERR.puts " >> No commands defined.\n\n"
  end
end

#put_command(cmd, redact: nil) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/pmgmt.rb', line 180

def put_command(cmd, redact: nil)
  if cmd.is_a?(String)
    command_string = "+ #{cmd}"
  else
    command_string = "+ #{cmd.join(" ")}"
  end
  command_to_echo = command_string.clone
  if redact
    command_to_echo.sub! redact, "*" * redact.length
  end
  STDERR.puts command_to_echo
end

#red_term_text(text) ⇒ Object



146
147
148
# File 'lib/pmgmt.rb', line 146

def red_term_text(text)
  "\033[0;31m#{text}\033[0m"
end

#run(cmd) ⇒ Status

Runs a command without echoing.

Returns:

  • (Status)

    command’s exit status.



253
254
255
256
257
258
# File 'lib/pmgmt.rb', line 253

def run(cmd)
  Open3.popen3(*cmd) do |i, o, e, t|
    i.close
    t.value
  end
end

#run_inline(cmd, redact: nil) ⇒ void

This method returns an undefined value.

Runs the given command “inline,” meaning that it will take over the terminal while running.

Parameters:

  • cmd (Array)

    Command to run.

  • redact: (String) (defaults to: nil)

    Occurrances of this string will be hidden from normal output.



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/pmgmt.rb', line 209

def run_inline(cmd, redact: nil)
  put_command(cmd, redact: redact)

  # `system`, by design (?!), hides stderr when the command fails.
  if ENV["PROJECTRB_USE_SYSTEM"] == "true"
    if not system(*cmd)
      exit $?.exitstatus
    end
  else
    pid = spawn(*cmd)
    Process.wait pid
    if $?.exited?
      if !$?.success?
        exit $?.exitstatus
      end
    else
      error "Command exited abnormally."
      exit 1
    end
  end
end

#run_inline_swallowing_interrupt(cmd) ⇒ Object

Similar to #run_inline but will prevent exiting when an interrupt is encountered.



232
233
234
235
236
237
# File 'lib/pmgmt.rb', line 232

def run_inline_swallowing_interrupt(cmd)
  begin
    run_inline cmd
  rescue Interrupt
  end
end

#run_or_fail(cmd, redact: nil) ⇒ Object

Runs a command and fails on non-success exit code.



240
241
242
243
244
245
246
247
248
249
# File 'lib/pmgmt.rb', line 240

def run_or_fail(cmd, redact: nil)
  put_command(cmd, redact: redact)
  Open3.popen3(*cmd) do |i, o, e, t|
    i.close
    if not t.value.success?
      STDERR.write red_term_text(e.read)
      exit t.value.exitstatus
    end
  end
end

#status(text) ⇒ void

This method returns an undefined value.

Prints “status” text (blue) on STDERR.



164
165
166
# File 'lib/pmgmt.rb', line 164

def status(text)
  STDERR.puts blue_term_text(text)
end

#warning(text) ⇒ void

This method returns an undefined value.

Prints “warning” text (yellow) on STDERR.



170
171
172
# File 'lib/pmgmt.rb', line 170

def warning(text)
  STDERR.puts yellow_term_text(text)
end

#yellow_term_text(text) ⇒ Object



154
155
156
# File 'lib/pmgmt.rb', line 154

def yellow_term_text(text)
  "\033[0;33m#{text}\033[0m"
end