cem_acpt 0.10.7 → 0.10.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f55a13b7ba6760d6d6b36ca313f7cddf637ae86b76ab48613de5938cb16f4d0e
4
- data.tar.gz: 63838f21061f45475f880a7e6966664b0a043938b2f74548f79afebaf9801eca
3
+ metadata.gz: 93cbd3354b97e498b8eba13d18b40a0294147c2e5d499ee3ce3a8c3cd809637d
4
+ data.tar.gz: d5879ac35af8d5fa25f4207b9efea2bc51fda8718a65a49aa38a87e7cb74e766
5
5
  SHA512:
6
- metadata.gz: 3679136cf4a7f393254856444f9c9bc922f608c86068f984620f8bcae3b5636bbcd22ce9fb2f72c80ba1e00e44df22ff0d445fb729f7d0f598ad3ce0dbed67e6
7
- data.tar.gz: a46c919e4d0418377d9d06e9fe4b5c253794d26ae8e5e5f43f9d8e6f72ac6da453fc326e9a86d918daacf35ef554ab19d56565aad4da76055de98b3922fcc536
6
+ metadata.gz: c02e967c85cc1b00840e2263f292af3a911d4675be2509228ad822d5014d586c7282cc81c6ea071d078970b7a7dcbdcf29755ea3c4befe56a1cb7209740d6fef
7
+ data.tar.gz: a0d1beae7b3ebe208a5310beccf5c8ba176f82a3840affad465cc7cf9ac47d76a1ae8b0eb8fc9f7d806302333bba3cd03b43b14ab9818212cefe57bc61330e92
@@ -12,7 +12,7 @@ on:
12
12
  jobs:
13
13
  tests:
14
14
  name: RSpec tests
15
- runs-on: ubuntu-20.04
15
+ runs-on: ubuntu-latest
16
16
  strategy:
17
17
  matrix:
18
18
  ruby:
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cem_acpt (0.10.7)
4
+ cem_acpt (0.10.8)
5
5
  async-http (>= 0.60, < 0.70)
6
6
  bcrypt_pbkdf (>= 1.0, < 2.0)
7
7
  deep_merge (>= 1.2, < 2.0)
@@ -11,49 +11,186 @@ module CemAcpt
11
11
  module Utils
12
12
  # Generic utilities for running local shell commands
13
13
  module Shell
14
- # Runs a command in a subshell and returns the Process::Status
15
- # and the string output of the command.
14
+ # Class to represent a single piece of output data, along with the stream it came from (stdout or stderr).
15
+ # This class is used to buffer output data from shell commands before writing it to the output stream, allowing
16
+ # for real-time logging and processing of the output.
17
+ class OutputData
18
+ attr_reader :data, :stream
19
+
20
+ # Initializes a new OutputData instance.
21
+ # @param data [String] The output data captured from the shell command
22
+ # @param stream [Symbol] The stream the data came from, either :stdout or :stderr. Defaults to :stdout.
23
+ def initialize(data, stream = :stdout)
24
+ @data = data
25
+ @stream = stream
26
+ end
27
+
28
+ # @return [String] The string representation of the output data, which is just the data itself.
29
+ def to_s
30
+ data
31
+ end
32
+ end
33
+
34
+ # Class to handle output from shell commands, allowing for real-time logging and buffering of output data.
35
+ # The Output class captures both stdout and stderr, and can be configured to combine them or keep them separate.
36
+ # It also supports logging debug messages if the provided output object supports it.
37
+ class Output
38
+ attr_writer :log_title
39
+
40
+ # Initializes a new Output instance.
41
+ # @param output [IO] An IO object that implements `:<<` to write the output of the command to in real time.
42
+ # Typically this is a Logger object. Defaults to $stdout.
43
+ # If the object responds to `:debug`, the command will be logged at the debug level.
44
+ # @param raise_on_fail [Boolean] Whether to raise an error if writing to the output fails. Defaults to true.
45
+ # @param combine_out_err [Boolean] Whether to combine the output and error streams into the output. If false,
46
+ # the stderr stream will not be written to the output stream or returned with the output string.
47
+ # Defaults to true.
48
+ def initialize(output: $stdout, raise_on_fail: true, combine_out_err: true)
49
+ @output = output
50
+ validate_output_object!
51
+ @buffer = []
52
+ @closed = false
53
+ @raise_on_fail = raise_on_fail
54
+ @combine_out_err = combine_out_err
55
+ end
56
+
57
+ # @return [StringIO] The StringIO object capturing stdout
58
+ def stdout
59
+ @io_out ||= StringIO.new
60
+ end
61
+
62
+ # @return [StringIO] The StringIO object capturing stderr
63
+ def stderr
64
+ @io_err ||= StringIO.new
65
+ end
66
+
67
+ # @return [String] The log title used for logging, defaults to "Output"
68
+ def log_title
69
+ @log_title ||= "Output"
70
+ end
71
+
72
+ # @return [Boolean] Whether the output is closed
73
+ def closed?
74
+ @closed
75
+ end
76
+
77
+ # Logs a debug message to the output if it supports :debug, otherwise writes it to the output stream.
78
+ # @param msg [String] The message to log as debug
79
+ def debug(msg)
80
+ write("DEBUG: #{msg}\n", :stderr)
81
+ if @output.respond_to? :debug
82
+ @output.debug(log_title) { msg }
83
+ end
84
+ end
85
+
86
+ # Writes data to the output stream and captures it in the buffer. Handles both stdout and stderr based on the stream parameter.
87
+ # @param data [String] The data to write to the output
88
+ # @param stream [Symbol] The stream to write to, either :stdout or :stderr. Defaults to :stdout.
89
+ def write(data, stream = :stdout)
90
+ raise IOError, "Cannot write to closed output" if closed?
91
+ begin
92
+ validate_stream!(stream)
93
+ newlines = data.count("\n")
94
+ if newlines == 1 && data.end_with?("\n")
95
+ data_obj = OutputData.new(data, stream)
96
+ @buffer << data_obj
97
+ flush
98
+ elsif newlines > 0
99
+ data.split("\n").each do |part|
100
+ data_obj = OutputData.new("#{part}\n", stream)
101
+ @buffer << data_obj
102
+ flush
103
+ end
104
+ else
105
+ @buffer << OutputData.new(data, stream)
106
+ end
107
+ rescue StandardError => e
108
+ raise e if @raise_on_fail
109
+
110
+ # If there's an error writing to the output, we still want to capture the output in the buffer and not lose it.
111
+ msg = "Error writing to output: #{e.message}. Captured output: #{data}"
112
+ @buffer << OutputData.new(msg, :stderr)
113
+ flush
114
+ end
115
+ end
116
+ alias << write
117
+
118
+ # Flushes the buffer to the output stream in order. If combine_out_err is true, stderr data will be written to the same stream as stdout.
119
+ def flush
120
+ return if @buffer.empty?
121
+
122
+ @buffer.each do |data_obj|
123
+ data = data_obj.to_s
124
+ stream = @combine_out_err ? :stdout : data_obj.stream
125
+ @output << data if @output
126
+ send(stream).write(data)
127
+ end
128
+ @buffer.clear
129
+ end
130
+
131
+ # Closes the output, flushing any remaining buffered data. If raise_on_fail is true, any errors during flushing will be raised.
132
+ def close
133
+ flush unless @buffer.empty?
134
+ @closed = true
135
+ end
136
+
137
+ private
138
+
139
+ def validate_output_object!
140
+ raise ArgumentError, "Output object must support :<< method" unless @output.respond_to?(:<<)
141
+ end
142
+
143
+ def validate_stream!(stream)
144
+ raise ArgumentError, "Stream must be :stdout or :stderr" unless [:stdout, :stderr].include?(stream)
145
+ end
146
+ end
147
+
148
+ # Runs a command in a subshell and returns the the string output of the command.
16
149
  # @param cmd [String] The command to run
17
150
  # @param env [Hash] A hash of environment variables to set
18
- # @param output [IO] An IO object that implements #:<< to write the output of the
151
+ # @param output [IO] An IO object that implements `:<<` to write the output of the
19
152
  # command to in real time. Typically this is a Logger object. Defaults to $stdout.
20
- # If the object responds to #:debug, the command will be logged at the debug level.
153
+ # If the object responds to `:debug`, the command will be logged at the debug level.
21
154
  # @param raise_on_fail [Boolean] Whether to raise an error if the command fails
22
155
  # @param combine_out_err [Boolean] Whether to combine the output and error streams into the output.
23
156
  # If false, the stderr stream will not be written to the output stream or returned with
24
157
  # the output string.
25
- # @return [String] The string output of the command, and the error output of the command
158
+ # @return [String] The string output of the command
26
159
  def self.run_cmd(cmd, env = {}, output: $stdout, raise_on_fail: true, combine_out_err: true)
27
- io_out = StringIO.new
28
- if output.respond_to?(:debug)
29
- output.debug('CemAcpt::Utils::Shell') { "Running command:\n\t#{cmd}\nWith environment:\n\t#{env}" }
30
- elsif output
31
- output << "Running command:\n\t#{cmd}\nWith environment:\n\t#{env}\n"
32
- end
33
- val = Open3.popen3(env, cmd) do |i, o, e, t|
34
- i.close
35
- o.sync = true
36
- e.sync = true
37
- output_thread = Thread.new do
38
- while (line = o.gets("\n"))
39
- output << line if output
40
- io_out.write(line) unless line.chomp.empty?
41
- end
42
- if combine_out_err
43
- while (line = e.gets("\n"))
44
- output << line if output && combine_out_err
45
- io_out.write(line) unless line.chomp.empty?
160
+ env.transform_keys!(&:to_s)
161
+ output = Output.new(output: output, raise_on_fail: raise_on_fail, combine_out_err: combine_out_err)
162
+ output.log_title = 'CemAcpt::Utils::Shell'
163
+ output.debug("Running command:\n\t#{cmd}\nWith environment:\n\t#{env}")
164
+ begin
165
+ val = Open3.popen3(env, cmd) do |i, o, e, t|
166
+ i.close
167
+ o.sync = true
168
+ e.sync = true
169
+ files = [o, e]
170
+ until files.all?(&:eof?)
171
+ ready = IO.select(files)
172
+ if ready
173
+ ready[0].each do |f|
174
+ data = f.read_nonblock(4096)
175
+ if f == o
176
+ output.write(data, :stdout)
177
+ elsif f == e
178
+ output.write(data, :stderr)
179
+ end
180
+ rescue IO::WaitReadable, EOFError
181
+ next
182
+ end
46
183
  end
47
184
  end
185
+ t.value
48
186
  end
49
- t.join
50
- output_thread.exit
51
- t.value
187
+ ensure
188
+ output.close unless output.closed?
52
189
  end
53
- io_string = io_out.string
54
- raise CemAcpt::ShellCommandError, "Error running command: #{cmd}\n#{io_string}" if raise_on_fail && !val.success?
55
-
56
- io_string
190
+ if raise_on_fail && !val.success?
191
+ raise CemAcpt::ShellCommandError, "Error running command: #{cmd}\n#{output.stdout.string}\n#{output.stderr.string}"
192
+ end
193
+ output.stdout.string
57
194
  end
58
195
 
59
196
  # Mimics the behavior of the `which` command.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CemAcpt
4
- VERSION = '0.10.7'
4
+ VERSION = '0.10.8'
5
5
  end
@@ -81,9 +81,9 @@ resource "google_compute_instance" "acpt-test-node" {
81
81
  }
82
82
 
83
83
  scheduling {
84
- preemptible = true
85
- automatic_restart = false
86
- provisioning_model = "SPOT"
84
+ preemptible = false
85
+ automatic_restart = true
86
+ provisioning_model = "STANDARD"
87
87
  instance_termination_action = "DELETE"
88
88
  max_run_duration {
89
89
  seconds = each.value.max_run_duration
@@ -80,9 +80,9 @@ resource "google_compute_instance" "acpt-test-node" {
80
80
  }
81
81
 
82
82
  scheduling {
83
- preemptible = true
84
- automatic_restart = false
85
- provisioning_model = "SPOT"
83
+ preemptible = false
84
+ automatic_restart = true
85
+ provisioning_model = "STANDARD"
86
86
  instance_termination_action = "DELETE"
87
87
  max_run_duration {
88
88
  seconds = each.value.max_run_duration
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cem_acpt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.7
4
+ version: 0.10.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - puppetlabs
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-03-31 00:00:00.000000000 Z
11
+ date: 2026-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: async-http
@@ -314,7 +314,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
314
314
  - !ruby/object:Gem::Version
315
315
  version: '0'
316
316
  requirements: []
317
- rubygems_version: 3.5.22
317
+ rubygems_version: 3.5.18
318
318
  signing_key:
319
319
  specification_version: 4
320
320
  summary: CEM Acceptance Tests