bwrap 1.0.0.pre.alpha5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,152 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bwrap/version"
4
+ require "bwrap/output"
5
+ require_relative "exceptions"
6
+ require_relative "execute"
7
+ require_relative "path"
4
8
 
5
- # Generic execution functionality.
9
+ # Provides methods to execute commands and handle its output.
10
+ #
11
+ # Output can be controlled by using log levels of Output module.
12
+ #
13
+ # @example Executing a command
14
+ # class MyClass
15
+ # include Bwrap::Execution
16
+ #
17
+ # def my_method
18
+ # execute %w{ ls /dev/null }
6
19
  module Bwrap::Execution
7
- # Nyan.
8
- end
20
+ include Bwrap::Output
21
+ include Bwrap::Execution::Path
22
+
23
+ # Actual implementation of execution command. Can be used when static method is needed.
24
+ #
25
+ # @note When an array is given as a command, empty strings are passed as empty arguments.
26
+ #
27
+ # This means that `[ "foo", "bar" ]` passes one argument to "foo" command, when
28
+ # `[ "foo", "", "bar" ]` passes two arguments.
29
+ #
30
+ # This may or may not be what is assumed, so it can’t be fixed here. It is up to the
31
+ # command to decide how to handle empty arguments.
32
+ #
33
+ # Returns pid of executed command if wait is false.
34
+ # Returns command output if wait is true.
35
+ #
36
+ # fail == If true, an error is raised in case the command returns failure code.
37
+ #
38
+ # @param error if :show, warn()s output of the command is shown if execution failed.
39
+ #
40
+ # @see #execute
41
+ def self.do_execute command,
42
+ fail: true,
43
+ wait: true,
44
+ log: true,
45
+ direct_output: false,
46
+ env: {},
47
+ clear_env: false,
48
+ error: nil,
49
+ log_callback: 2,
50
+ rootcmd: nil
51
+ command = Execute.format_command command, rootcmd: rootcmd
52
+ Execute.handle_logging command, log_callback: log_callback, log: log, dry_run: @dry_run
53
+ return if @dry_run || Bwrap::Execution::Execute.dry_run
54
+
55
+ Execute.open_pipes direct_output
56
+
57
+ # If command is an array, there can’t be arrays inside the array.
58
+ # For convenience, the array is flattened here, so callers can construct commands more easily.
59
+ if command.respond_to? :flatten!
60
+ command.flatten!
61
+ end
62
+
63
+ # If command is string, splat operator (the *) does not do anything. If array, it expand the arguments.
64
+ # This causes spawning work correctly, as that’s how spawn() expects to have the argu
65
+ pid = spawn(env, *command, err: [ :child, :out ], out: Execute.w, unsetenv_others: clear_env)
66
+ output = Execute.finish_execution(log: log, wait: wait, direct_output: direct_output)
67
+ return pid unless wait
68
+
69
+ # This is instant return, but allows us to have $?/$CHILD_STATUS set.
70
+ Process.wait pid
71
+ @last_status = $CHILD_STATUS
72
+
73
+ output = Execute.process_output output: output
74
+ Execute.handle_execution_fail fail: fail, error: error, output: output
75
+ output
76
+ ensure
77
+ Execute.clean_variables
78
+ end
79
+
80
+ # Returns Process::Status instance of last execution.
81
+ #
82
+ # @note This only is able to return the status if wait is true, as otherwise caller is assumed to
83
+ # handle execution flow.
84
+ def self.last_status
85
+ @last_status
86
+ end
87
+
88
+ # Execute a command.
89
+ #
90
+ # This method can be used by including Execution module in a class that should be able to
91
+ # execute commands.
92
+ #
93
+ # @see .do_execute .do_execute for documentation of argument syntax
94
+ private def execute *args, **kwargs
95
+ # Mangle proper location to error message.
96
+ if kwargs.is_a? Hash
97
+ kwargs[:log_callback] = 3
98
+ else
99
+ kwargs = { log_callback: 3 }
100
+ end
101
+ Bwrap::Execution.do_execute(*args, **kwargs)
102
+ end
103
+
104
+ # Same as ::execute, but uses log: false to avoid unnecessary output when we’re just getting a
105
+ # value for internal needs.
106
+ #
107
+ # Defaults to fail: false, since when one just wants to get the value, there is not that much
108
+ # need to unconditionally die if getting bad exit code.
109
+ private def execvalue *args, fail: false, log: false, **kwargs
110
+ # This logging handling is a bit of duplication from execute(), but to be extra safe, it is duplicated.
111
+ # The debug message contents will always be evaluated, so can just do it like this.
112
+ log_command = args[0].respond_to?(:join) && args[0].join(" ") || args[0]
113
+ log_command =
114
+ if log_command.frozen?
115
+ log_command.dup.force_encoding("UTF-8")
116
+ else
117
+ log_command.force_encoding("UTF-8")
118
+ end
119
+ if @dry_run
120
+ puts "Would execvalue “#{log_command}” at #{caller_locations(1, 1)[0]}"
121
+ return
122
+ end
123
+ trace "Execvaluing “#{log_command}” at #{caller_locations(1, 1)[0]}"
124
+ execute(*args, fail: fail, log: log, **kwargs)
125
+ end
126
+
127
+ private def exec_success?
128
+ $CHILD_STATUS.success?
129
+ end
130
+
131
+ private def exec_failure?
132
+ !exec_success?
133
+ end
134
+
135
+ # When running through bundler, don’t use whatever it defines as we’re running inside chroot.
136
+ private def clean_execute
137
+ if (Bundler&.bundler_major_version) >= 2
138
+ Bundler.with_unbundled_env do
139
+ yield 2
140
+ end
141
+ elsif Bundler&.bundler_major_version == 1
142
+ Bundler.with_clean_env do
143
+ yield 1
144
+ end
145
+ else
146
+ yield nil
147
+ end
148
+ rescue NameError
149
+ # If NameError is thrown, no Bundler is available.
150
+ yield nil
151
+ end
152
+ end
@@ -1,7 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bwrap/version"
4
- require_relative "execution"
4
+
5
+ # Just declaring the module here so full Execution module
6
+ # doesn’t need to be required just to have labels.
7
+ #
8
+ # Most users probably should just require bwrap/execution directly
9
+ # instead of this file, but bwrap/output.rb benefits from this.
10
+ module Bwrap::Execution
11
+ end
5
12
 
6
13
  # Exit codes for exit status handling.
7
14
  #
@@ -1,15 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bwrap/output"
4
+ require_relative "exceptions"
4
5
 
5
6
  # Path checking methods.
6
7
  module Bwrap::Execution::Path
7
- # @api internal
8
+ # Utilities to handle environment path operations.
9
+ #
10
+ # @api private
8
11
  class Environment
9
- def self.each_env_path command
12
+ # Loop through each path in global PATH environment variable to
13
+ # perform an operation in each path, for example to resolve
14
+ # absolute path to a command.
15
+ #
16
+ # NOTE: This has nothing to do with {Bwrap::Config#env_paths}, as the
17
+ # env paths looped are what the system has defined, in ENV["PATH"].
18
+ #
19
+ # Should be cross-platform.
20
+ #
21
+ # @yield Command appended to each path in PATH environment variable
22
+ # @yieldparam path [String] Full path to executable
23
+ def self.each_env_path command, env_path_var: ENV["PATH"]
10
24
  exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [ "" ]
11
25
 
12
- ENV["PATH"].split(File::PATH_SEPARATOR).each do |env_path|
26
+ env_path_var.split(File::PATH_SEPARATOR).each do |env_path|
13
27
  exts.each do |ext|
14
28
  exe = File.join(env_path, "#{command}#{ext}")
15
29
  yield exe
@@ -20,8 +34,12 @@ module Bwrap::Execution::Path
20
34
 
21
35
  # Check if requested program can be found.
22
36
  #
23
- # Should work cross-platform and in restricted environents pretty well.
24
- private def command_available? command
37
+ # Should work cross-platform and in restricted environments pretty well.
38
+ #
39
+ # @param command [String] executable to be resolved
40
+ # @param env_path_var [String] PATH environment variable as string.
41
+ # Defaults to `ENV["PATH"]`
42
+ private def command_available? command, env_path_var: ENV["PATH"]
25
43
  # Special handling for absolute paths.
26
44
  path = Pathname.new command
27
45
  if path.absolute?
@@ -32,7 +50,7 @@ module Bwrap::Execution::Path
32
50
  return false
33
51
  end
34
52
 
35
- Bwrap::Execution::Path::Environment.each_env_path command do |exe|
53
+ Bwrap::Execution::Path::Environment.each_env_path command, env_path_var: env_path_var do |exe|
36
54
  return true if File.executable?(exe) && !File.directory?(exe)
37
55
  end
38
56
 
@@ -40,7 +58,9 @@ module Bwrap::Execution::Path
40
58
  end
41
59
 
42
60
  # Returns path to given executable.
43
- private def which command, fail: true
61
+ #
62
+ # @param (see #command_available?)
63
+ private def which command, fail: true, env_path_var: ENV["PATH"]
44
64
  # Special handling for absolute paths.
45
65
  path = Pathname.new command
46
66
  if path.absolute?
@@ -48,17 +68,17 @@ module Bwrap::Execution::Path
48
68
  return command
49
69
  end
50
70
 
51
- raise CommandNotFound.new command: command if fail
71
+ raise Bwrap::Execution::CommandNotFound.new command: command if fail
52
72
 
53
73
  return nil
54
74
  end
55
75
 
56
- Bwrap::Execution::Path::Environment.each_env_path command do |exe|
76
+ Bwrap::Execution::Path::Environment.each_env_path command, env_path_var: env_path_var do |exe|
57
77
  return exe if File.executable?(exe) && !File.directory?(exe)
58
78
  end
59
79
 
60
80
  return nil unless fail
61
81
 
62
- raise CommandNotFound.new command: command
82
+ raise Bwrap::Execution::CommandNotFound.new command: command
63
83
  end
64
84
  end
@@ -1,165 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "bwrap/version"
4
- require "bwrap/output"
5
- require_relative "execution/execute"
6
- require_relative "execution/path"
3
+ require_relative "bwrap_module"
7
4
 
8
- # @abstract Module to be included in a class that needs to execute commands.
9
- #
10
- # Methods to execute processes.
5
+ # Declare Execution module here so Bwrap::Execution module is
6
+ # already declared for Execution module classes, to avoid
7
+ # a circular dependency.
11
8
  module Bwrap::Execution
12
- include Bwrap::Output
13
- include Bwrap::Execution::Path
14
-
15
- # Unspecified execution related error.
16
- class CommandError < StandardError
17
- end
18
-
19
- # Signifies that command execution has failed.
20
- class ExecutionFailed < CommandError
21
- end
22
-
23
- # Thrown if given command was not found.
24
- class CommandNotFound < CommandError
25
- # Command that was looked at.
26
- attr_reader :command
27
-
28
- def initialize command:
29
- @command = command
30
- msg = "Failed to find #{command} from PATH."
31
-
32
- super msg
33
- end
34
- end
35
-
36
- # Actual implementation of execution command. Can be used when static method is needed.
37
- #
38
- # @note When an array is given as a command, empty strings are passed as empty arguments.
39
- #
40
- # This means that `[ "foo", "bar" ]` passes one argument to "foo" command, when
41
- # `[ "foo", "", "bar" ]` passes two arguments.
42
- #
43
- # This may or may not be what is assumed, so it can’t be fixed here. It is up to the
44
- # command to decide how to handle empty arguments.
45
- #
46
- # Returns pid of executed command if wait is false.
47
- # Returns command output if wait is true.
48
- #
49
- # fail == If true, an error is raised in case the command returns failure code.
50
- #
51
- # @param error if :show, warn()s output of the command is shown if execution failed.
52
- #
53
- # @see #execute
54
- def self.do_execute command,
55
- fail: true,
56
- wait: true,
57
- log: true,
58
- direct_output: false,
59
- env: {},
60
- clear_env: false,
61
- error: nil,
62
- log_callback: 2,
63
- rootcmd: nil
64
- command = Execute.format_command command, rootcmd: rootcmd
65
- Execute.handle_logging command, log_callback: log_callback, log: log, dry_run: @dry_run
66
- return if @dry_run || Bwrap::Execution::Execute.dry_run
67
-
68
- Execute.open_pipes direct_output
69
-
70
- # If command is an array, there can’t be arrays inside the array.
71
- # For convenience, the array is flattened here, so callers can construct commands more easily.
72
- if command.respond_to? :flatten!
73
- command.flatten!
74
- end
75
-
76
- # If command is string, splat operator (the *) does not do anything. If array, it expand the arguments.
77
- # This causes spawning work correctly, as that’s how spawn() expects to have the argu
78
- pid = spawn(env, *command, err: [ :child, :out ], out: Execute.w, unsetenv_others: clear_env)
79
- output = Execute.finish_execution(log: log, wait: wait, direct_output: direct_output)
80
- return pid unless wait
81
-
82
- # This is instant return, but allows us to have $?/$CHILD_STATUS set.
83
- Process.wait pid
84
- @last_status = $CHILD_STATUS
85
-
86
- output = Execute.process_output output: output
87
- Execute.handle_execution_fail fail: fail, error: error, output: output
88
- output
89
- ensure
90
- Execute.clean_variables
91
- end
92
-
93
- # Returns Process::Status instance of last execution.
94
- #
95
- # @note This only is able to return the status if wait is true, as otherwise caller is assumed to
96
- # handle execution flow.
97
- def self.last_status
98
- @last_status
99
- end
100
-
101
- # Execute a command.
102
- #
103
- # This method can be used by including Execution module in a class that should be able to
104
- # execute commands.
105
- #
106
- # @see .do_execute .do_execute for documentation of argument syntax
107
- private def execute *args
108
- # Mangle proper location to error message.
109
- if args.last.is_a? Hash
110
- args.last[:log_callback] = 3
111
- else
112
- args << { log_callback: 3 }
113
- end
114
- Bwrap::Execution.do_execute(*args)
115
- end
116
-
117
- # Same as ::execute, but uses log: false to avoid unnecessary output when we’re just getting a
118
- # value for internal needs.
119
- #
120
- # Defaults to fail: false, since when one just wants to get the value, there is not that much
121
- # need to unconditionally die if getting bad exit code.
122
- private def execvalue *args, fail: false, rootcmd: nil, env: {}
123
- # This logging handling is a bit of duplication from execute(), but to be extra safe, it is duplicated.
124
- # The debug message contents will always be evaluated, so can just do it like this.
125
- log_command = args[0].respond_to?(:join) && args[0].join(" ") || args[0]
126
- log_command =
127
- if log_command.frozen?
128
- log_command.dup.force_encoding("UTF-8")
129
- else
130
- log_command.force_encoding("UTF-8")
131
- end
132
- if @dry_run
133
- puts "Would execvalue “#{log_command}” at #{caller_locations(1, 1)[0]}"
134
- return
135
- end
136
- trace "Execvaluing “#{log_command}” at #{caller_locations(1, 1)[0]}"
137
- execute(*args, fail: fail, log: false, rootcmd: rootcmd, env: env)
138
- end
139
-
140
- private def exec_success?
141
- $CHILD_STATUS.success?
142
- end
143
-
144
- private def exec_failure?
145
- !exec_success?
146
- end
147
-
148
- # When running through bundler, don’t use whatever it defines as we’re running inside chroot.
149
- private def clean_execute
150
- if (Bundler&.bundler_major_version) >= 2
151
- Bundler.with_unbundled_env do
152
- yield 2
153
- end
154
- elsif Bundler&.bundler_major_version == 1
155
- Bundler.with_clean_env do
156
- yield 1
157
- end
158
- else
159
- yield nil
160
- end
161
- rescue NameError
162
- # If NameError is thrown, no Bundler is available.
163
- yield nil
164
- end
165
9
  end
10
+
11
+ require_relative "execution/execution"
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "output"
4
-
5
3
  # Methods to color CLI output.
6
4
  module Bwrap::Output::Colors
7
5
  # Outputs ANSI true color sequence.
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # force_encoding modifies string, so can’t freeze strings.
4
-
5
- require_relative "output"
6
-
7
3
  # Logging methods.
4
+ #
5
+ # @note One should require "bwrap/output" instead of this file directly, even
6
+ # if using only methods from this class.
7
+ #
8
+ # This is because Bwrap::Output module would be missing, or there could be
9
+ # a circular dependency, which is always bad, even if Ruby would break it for you.
8
10
  class Bwrap::Output::Log
9
11
  @@log_file = nil
10
12
 
@@ -15,11 +17,17 @@ class Bwrap::Output::Log
15
17
 
16
18
  # Writes given string to log.
17
19
  def self.write_to_log str
20
+ # Guard against invalid input.
21
+ return unless str.respond_to? :force_encoding
22
+
18
23
  @@log_file&.write str.dup.force_encoding("UTF-8")
19
24
  end
20
25
 
21
26
  # Writes given string to log.
22
27
  def self.puts_to_log str
28
+ # Guard against invalid input.
29
+ return unless str.respond_to? :force_encoding
30
+
23
31
  @@log_file&.puts str.dup.force_encoding("UTF-8")
24
32
  end
25
33
 
@@ -31,6 +39,10 @@ class Bwrap::Output::Log
31
39
 
32
40
  # Starts logging to given file.
33
41
  def self.log_to_file log_path
42
+ unless File.writable? log_path
43
+ warn "Given log file #{log_path} is not writable by current user."
44
+ return
45
+ end
34
46
  log_file = File.open log_path, "w"
35
47
 
36
48
  # In default mode, log messages disappears as Ruby’s own buffer gets full.
@@ -40,7 +52,7 @@ class Bwrap::Output::Log
40
52
  @@log_file = log_file
41
53
 
42
54
  at_exit do
43
- ::Bwrap::Output::Log.close_log_file
55
+ Bwrap::Output::Log.close_log_file
44
56
  end
45
57
  end
46
58
  end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Have variables like $CHILD_STATUS which is alias of $?.
4
+ require "English"
5
+
6
+ require "bwrap/bwrap_module"
7
+ require "bwrap/execution/labels"
8
+
9
+ require_relative "levels"
10
+ require_relative "log"
11
+
12
+ # Methods useful for output handling.
13
+ #
14
+ # There is four different log levels:
15
+ #
16
+ # @!description_list
17
+ # @term error
18
+ # @description
19
+ # Causes execution to halt either to exit with requested code or,
20
+ # if requested, raises an exception.
21
+ # @term warning
22
+ # @description Outputs given text to STDERR.
23
+ # @term verbose
24
+ # @description Outputs given text if verbose, debug or trace flag is set.
25
+ # @term debug
26
+ # @description Outputs given text if debug or trace flag is set.
27
+ # @term trace
28
+ # @description Outputs given text if trace flag is set.
29
+ #
30
+ # Output levels can be enabled with {.handle_output_options}.
31
+ #
32
+ # When using {Bwrap::Bwrap}, {Bwrap::Bwrap#parse_command_line_arguments}
33
+ # causes output levels to be set if relevant CLI arguments have been
34
+ # given. TODO: Add documentation about CLI args somewhere. Maybe README?
35
+ module Bwrap::Output
36
+ # @see #verbose?
37
+ def self.verbose?
38
+ Bwrap::Output::Levels.verbose?
39
+ end
40
+
41
+ # @see #debug?
42
+ def self.debug?
43
+ Bwrap::Output::Levels.debug?
44
+ end
45
+
46
+ # @see #trace?
47
+ def self.trace?
48
+ Bwrap::Output::Levels.trace?
49
+ end
50
+
51
+ # Takes hash of options received from Optimist and checks output related flags.
52
+ def self.handle_output_options options
53
+ Bwrap::Output::Levels.handle_output_options options
54
+ end
55
+
56
+ # Handler used by #trace to output given string.
57
+ def self.trace_output str, raw: false, log_callback: 1
58
+ return unless trace?
59
+
60
+ if raw
61
+ print str
62
+ else
63
+ out = Bwrap::Output::Levels.trace_print_formatted str, log_callback: (log_callback + 1)
64
+ end
65
+ Bwrap::Output::Log.puts_to_log out || str
66
+ end
67
+
68
+ # Handler used by #debug to output given string.
69
+ def self.debug_output str, raw: false, log_callback: 1
70
+ return unless debug?
71
+
72
+ if raw
73
+ print str
74
+ else
75
+ out = Bwrap::Output::Levels.debug_print_formatted str, log_callback: (log_callback + 1)
76
+ end
77
+ Bwrap::Output::Log.puts_to_log out || str
78
+ end
79
+
80
+ # Handler used by #verb to output given string.
81
+ def self.verb_output str, raw: false, log_callback: 1
82
+ return unless verbose?
83
+
84
+ if raw
85
+ print str
86
+ else
87
+ out = Bwrap::Output::Levels.verbose_print_formatted str, log_callback: (log_callback + 1)
88
+ end
89
+ Bwrap::Output::Log.puts_to_log out || str
90
+ end
91
+
92
+ # Handler used by #warn to output given string.
93
+ def self.warn_output str, raw: false, log_callback: 1
94
+ if raw
95
+ print str
96
+ else
97
+ out = Bwrap::Output::Levels.warning_print_formatted str, log_callback: (log_callback + 1)
98
+ end
99
+ Bwrap::Output::Log.puts_to_log out || str
100
+ end
101
+
102
+ # Aborts current process.
103
+ #
104
+ # Use this instead of Ruby’s #exit in order to filter out dummy #exit calls from the code.
105
+ def self.error_output str = nil, label: :unspecified, log_callback: 1, raise_exception: false
106
+ unless str.nil?
107
+ out = Bwrap::Output::Levels.error_print_formatted str, log_callback: (log_callback + 1)
108
+ Bwrap::Output::Log.puts_to_log out
109
+ end
110
+
111
+ exit_code = Bwrap::Execution::Labels.resolve_exit_code(label)
112
+ raise str if raise_exception
113
+
114
+ exit exit_code
115
+ end
116
+
117
+ # @return true if --verbose, --debug or --trace has been passed, false if not.
118
+ private def verbose?
119
+ Bwrap::Output::Levels.verbose?
120
+ end
121
+
122
+ # @return true if --debug or --trace has been passed, false if not.
123
+ private def debug?
124
+ Bwrap::Output::Levels.debug?
125
+ end
126
+
127
+ # @return true if --trace has been passed, false if not.
128
+ private def trace?
129
+ Bwrap::Output::Levels.trace?
130
+ end
131
+
132
+ # @!group Outputters
133
+
134
+ # Outputs given string if trace flag has been set.
135
+ #
136
+ # Output flags can be set with {.handle_output_options}.
137
+ #
138
+ # @param str String to be outputted
139
+ # @param raw [Boolean] If true, disables output formatting
140
+ private def trace str, raw: false
141
+ Bwrap::Output.trace_output(str, raw: raw, log_callback: 2)
142
+ end
143
+
144
+ # Outputs given string if debug flag has been set.
145
+ #
146
+ # Output flags can be set with {.handle_output_options}.
147
+ #
148
+ # @param str String to be outputted
149
+ # @param raw [Boolean] If true, disables output formatting
150
+ private def debug str, raw: false
151
+ Bwrap::Output.debug_output(str, raw: raw, log_callback: 2)
152
+ end
153
+
154
+ # Outputs given string if verbose flag has been set.
155
+ #
156
+ # Output flags can be set with {.handle_output_options}.
157
+ #
158
+ # @param str String to be outputted
159
+ # @param raw [Boolean] If true, disables output formatting
160
+ private def verb str, raw: false
161
+ Bwrap::Output.verb_output(str, raw: raw, log_callback: 2)
162
+ end
163
+
164
+ # Outputs given string to `$stderr`.
165
+ #
166
+ # @param str String to be outputted
167
+ # @param raw [Boolean] If true, disables output formatting
168
+ private def warn str, raw: false
169
+ Bwrap::Output.warn_output(str, raw: raw, log_callback: 2)
170
+ end
171
+
172
+ # Outputs given string to `$stderr` and halts execution.
173
+ #
174
+ # @param str String to be outputted
175
+ # @param label [Symbol] Exit label accepted by {Bwrap::Execution.resolve_exit_code}
176
+ # @param raise_exception [Boolean] if true, an exception is raised instead of just existing with exit code.
177
+ private def error str = nil, label: :unspecified, raise_exception: false
178
+ Bwrap::Output.error_output(str, label: label, log_callback: 2, raise_exception: raise_exception)
179
+ end
180
+
181
+ # @!endgroup
182
+ end