kettle-dev 3.0.0 → 3.0.1
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +16 -1
- data/README.md +1 -1
- data/lib/kettle/dev/interactive_release_command.rb +56 -27
- data/lib/kettle/dev/release_cli.rb +4 -1
- data/lib/kettle/dev/release_secrets.rb +22 -2
- data/lib/kettle/dev/version.rb +1 -1
- data.tar.gz.sig +4 -4
- metadata +4 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 335bd9651ff7bbe5381c8e050a26f070c6accd8054ab581226ba3355c8a4aa6e
|
|
4
|
+
data.tar.gz: 7f59238412adde48ba5f2b524b08e4ce77a5348c81d0984e44013e6ce905b2dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94e3d356b963078c8dd46d50d0f2b7ed92f656327bd3f766618617b56172ae0051dc32f6469cfd5c9faf539e00a4bbdf123947dd8febbd10c34172f51a2efc2d
|
|
7
|
+
data.tar.gz: d2eee5766a478c4846f05969ac4fcbf4614793740911c029219d85fb446fd308f9c376c5fa8d68edbdc71bd144dba09a1ad927c5915278c7933d0fc50ac74dca
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGELOG.md
CHANGED
|
@@ -30,6 +30,19 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
30
30
|
|
|
31
31
|
### Security
|
|
32
32
|
|
|
33
|
+
## [3.0.1] - 2026-08-10
|
|
34
|
+
|
|
35
|
+
- TAG: [v3.0.1][3.0.1t]
|
|
36
|
+
- COVERAGE: 91.87% -- 5602/6098 lines in 47 files
|
|
37
|
+
- BRANCH COVERAGE: 76.29% -- 2117/2775 branches in 47 files
|
|
38
|
+
- 50.64% documented
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- Run 1Password authorization keepalives through the interactive terminal so release prompts remain visible.
|
|
43
|
+
|
|
44
|
+
- Re-normalize release lockfiles after release tasks before commit and CI validation.
|
|
45
|
+
|
|
33
46
|
## [3.0.0] - 2026-08-09
|
|
34
47
|
|
|
35
48
|
- TAG: [v3.0.0][3.0.0t]
|
|
@@ -3401,7 +3414,9 @@ Please file a bug if you notice a violation of semantic versioning.
|
|
|
3401
3414
|
- Selecting will run the selected workflow via `act`
|
|
3402
3415
|
- This may move to its own gem in the future.
|
|
3403
3416
|
|
|
3404
|
-
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.
|
|
3417
|
+
[Unreleased]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.1...HEAD
|
|
3418
|
+
[3.0.1]: https://github.com/kettle-dev/kettle-dev/compare/v3.0.0...v3.0.1
|
|
3419
|
+
[3.0.1t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.1
|
|
3405
3420
|
[3.0.0]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.28...v3.0.0
|
|
3406
3421
|
[3.0.0t]: https://github.com/kettle-dev/kettle-dev/releases/tag/v3.0.0
|
|
3407
3422
|
[2.5.28]: https://github.com/kettle-dev/kettle-dev/compare/v2.5.27...v2.5.28
|
data/README.md
CHANGED
|
@@ -1007,7 +1007,7 @@ Thanks for RTFM. ☺️
|
|
|
1007
1007
|
[📌gitmoji]: https://gitmoji.dev
|
|
1008
1008
|
[📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
|
|
1009
1009
|
[🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
|
|
1010
|
-
[🧮kloc-img]: https://img.shields.io/badge/KLOC-6.
|
|
1010
|
+
[🧮kloc-img]: https://img.shields.io/badge/KLOC-6.098-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
|
|
1011
1011
|
[🔐security]: https://github.com/kettle-dev/kettle-dev/blob/main/SECURITY.md
|
|
1012
1012
|
[🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
|
|
1013
1013
|
[📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
|
|
@@ -1,39 +1,54 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
|
+
require "shellwords"
|
|
4
5
|
|
|
5
6
|
module Kettle
|
|
6
7
|
module Dev
|
|
7
8
|
class InteractiveReleaseCommand
|
|
8
|
-
def initialize(secrets_provider:, input: $stdin, output: $stdout, error: $stderr, secret_event_handler: nil)
|
|
9
|
+
def initialize(secrets_provider:, input: $stdin, output: $stdout, error: $stderr, secret_event_handler: nil, forward_stdin: false)
|
|
9
10
|
@secrets_provider = secrets_provider
|
|
10
11
|
@input = input
|
|
11
12
|
@output = output
|
|
12
13
|
@error = error
|
|
13
14
|
@secret_event_handler = secret_event_handler
|
|
15
|
+
@forward_stdin = forward_stdin
|
|
14
16
|
@gem_signing_passphrase = nil
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def call(env, cmd)
|
|
18
|
-
|
|
20
|
+
call_argv(env, Shellwords.split(cmd.to_s))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def call_argv(env, argv)
|
|
24
|
+
return call_with_pty(env, argv) if pty_available?
|
|
19
25
|
|
|
20
|
-
call_with_open3(env,
|
|
26
|
+
call_with_open3(env, argv)
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
private
|
|
24
30
|
|
|
25
31
|
attr_reader :secrets_provider, :secret_event_handler
|
|
26
32
|
|
|
27
|
-
def call_with_pty(env,
|
|
33
|
+
def call_with_pty(env, argv)
|
|
28
34
|
stdout_str = +""
|
|
35
|
+
prompt_buffer = +""
|
|
29
36
|
status = nil
|
|
30
|
-
PTY.spawn(env,
|
|
37
|
+
PTY.spawn(env, *argv) do |output, input, pid|
|
|
31
38
|
begin
|
|
32
39
|
loop do
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
40
|
+
readers = [output]
|
|
41
|
+
readers << @input if @forward_stdin && @input.tty?
|
|
42
|
+
IO.select(readers).first.each do |reader|
|
|
43
|
+
if reader.equal?(@input)
|
|
44
|
+
input.write(@input.readpartial(1024))
|
|
45
|
+
else
|
|
46
|
+
chunk = output.readpartial(1024)
|
|
47
|
+
stdout_str << chunk
|
|
48
|
+
@output.print(chunk)
|
|
49
|
+
prompt_buffer = handle_prompt(input, chunk, prompt_buffer: prompt_buffer)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
37
52
|
end
|
|
38
53
|
rescue Errno::EIO
|
|
39
54
|
# PTY raises EIO when the child process exits after closing the slave.
|
|
@@ -47,27 +62,33 @@ module Kettle
|
|
|
47
62
|
[stdout_str, "", status]
|
|
48
63
|
end
|
|
49
64
|
|
|
50
|
-
def call_with_open3(env,
|
|
65
|
+
def call_with_open3(env, argv)
|
|
51
66
|
stdout_str = +""
|
|
52
67
|
stderr_str = +""
|
|
68
|
+
prompt_buffer = +""
|
|
53
69
|
status = nil
|
|
54
|
-
Open3.popen3(env,
|
|
70
|
+
Open3.popen3(env, *argv) do |input, output, error, wait_thread|
|
|
55
71
|
readers = [output, error]
|
|
72
|
+
readers << @input if @forward_stdin && @input.tty?
|
|
56
73
|
until readers.empty?
|
|
57
74
|
ready = IO.select(readers)
|
|
58
75
|
ready.first.each do |reader|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
if reader.equal?(@input)
|
|
77
|
+
input.write(@input.readpartial(1024))
|
|
78
|
+
else
|
|
79
|
+
begin
|
|
80
|
+
chunk = reader.readpartial(1024)
|
|
81
|
+
if reader.equal?(output)
|
|
82
|
+
stdout_str << chunk
|
|
83
|
+
@output.print(chunk)
|
|
84
|
+
else
|
|
85
|
+
stderr_str << chunk
|
|
86
|
+
@error.print(chunk)
|
|
87
|
+
end
|
|
88
|
+
prompt_buffer = handle_prompt(input, chunk, prompt_buffer: prompt_buffer)
|
|
89
|
+
rescue EOFError
|
|
90
|
+
readers.delete(reader)
|
|
67
91
|
end
|
|
68
|
-
handle_prompt(input, chunk)
|
|
69
|
-
rescue EOFError
|
|
70
|
-
readers.delete(reader)
|
|
71
92
|
end
|
|
72
93
|
end
|
|
73
94
|
end
|
|
@@ -76,13 +97,21 @@ module Kettle
|
|
|
76
97
|
[stdout_str, stderr_str, status]
|
|
77
98
|
end
|
|
78
99
|
|
|
79
|
-
def handle_prompt(input, chunk)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
def handle_prompt(input, chunk, prompt_buffer: nil)
|
|
101
|
+
prompt_buffer = prompt_buffer.to_s.dup
|
|
102
|
+
prompt_buffer << chunk.to_s
|
|
103
|
+
prompt_buffer = prompt_buffer.byteslice([prompt_buffer.bytesize - 4096, 0].max, 4096).to_s
|
|
104
|
+
|
|
105
|
+
if otp_prompt?(prompt_buffer)
|
|
106
|
+
if otp_response_prompt?(prompt_buffer)
|
|
107
|
+
write_secret(input, label: "RubyGems MFA code", source: "rubygems_otp") { secrets_provider.rubygems_otp }
|
|
108
|
+
return +""
|
|
109
|
+
end
|
|
110
|
+
return prompt_buffer
|
|
83
111
|
end
|
|
84
112
|
|
|
85
|
-
write_secret(input, label: "gem signing passphrase", source: "gem_signing_passphrase") { gem_signing_passphrase } if signing_password_prompt?(
|
|
113
|
+
write_secret(input, label: "gem signing passphrase", source: "gem_signing_passphrase") { gem_signing_passphrase } if signing_password_prompt?(prompt_buffer)
|
|
114
|
+
prompt_buffer
|
|
86
115
|
end
|
|
87
116
|
|
|
88
117
|
def gem_signing_passphrase
|
|
@@ -622,7 +622,10 @@ module Kettle
|
|
|
622
622
|
end
|
|
623
623
|
|
|
624
624
|
def prepare_release_lockfiles_for_commit!
|
|
625
|
-
|
|
625
|
+
# Release setup and documentation tasks can re-materialize local PATH
|
|
626
|
+
# sources in the parent development bundle after the preflight reset.
|
|
627
|
+
# Always normalize again at the commit boundary.
|
|
628
|
+
reset_release_lockfiles!(stage: "before release prep commit")
|
|
626
629
|
validate_release_lockfiles!(stage: "before release prep commit")
|
|
627
630
|
end
|
|
628
631
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
|
+
require "kettle/dev/interactive_release_command"
|
|
4
5
|
require "kettle/dev/release_notifier"
|
|
5
6
|
|
|
6
7
|
module Kettle
|
|
@@ -73,7 +74,7 @@ module Kettle
|
|
|
73
74
|
argv = [op_cli, "account", "get"]
|
|
74
75
|
account = string_config("account")
|
|
75
76
|
argv.concat(["--account", account]) unless account.empty?
|
|
76
|
-
run_op(argv, purpose: "authorization keepalive", elapsed: elapsed)
|
|
77
|
+
run_op(argv, purpose: "authorization keepalive", elapsed: elapsed, interactive: true)
|
|
77
78
|
true
|
|
78
79
|
end
|
|
79
80
|
|
|
@@ -112,9 +113,11 @@ module Kettle
|
|
|
112
113
|
required_config("cli")
|
|
113
114
|
end
|
|
114
115
|
|
|
115
|
-
def run_op(argv, purpose:, elapsed: nil)
|
|
116
|
+
def run_op(argv, purpose:, elapsed: nil, interactive: false)
|
|
116
117
|
elapsed_suffix = elapsed.to_s.empty? ? "" : " (elapsed #{elapsed})"
|
|
117
118
|
Kettle::Dev::ReleaseNotifier.alert("1Password #{purpose} lookup starting#{elapsed_suffix}; watch for authorization prompt.")
|
|
119
|
+
return run_interactive_op(argv, purpose: purpose) if interactive
|
|
120
|
+
|
|
118
121
|
stdout, stderr, status = Open3.capture3(*argv)
|
|
119
122
|
return stdout.to_s.strip if status.success? && !stdout.to_s.strip.empty?
|
|
120
123
|
|
|
@@ -125,6 +128,23 @@ module Kettle
|
|
|
125
128
|
raise Kettle::Dev::Error, "1Password CLI executable #{argv.first.inspect} was not found"
|
|
126
129
|
end
|
|
127
130
|
|
|
131
|
+
def run_interactive_op(argv, purpose:)
|
|
132
|
+
_stdout, stderr, status = Kettle::Dev::InteractiveReleaseCommand.new(
|
|
133
|
+
secrets_provider: Provider.new,
|
|
134
|
+
input: $stdin,
|
|
135
|
+
output: $stdout,
|
|
136
|
+
error: $stderr,
|
|
137
|
+
forward_stdin: true
|
|
138
|
+
).call_argv({}, argv)
|
|
139
|
+
return if status.success?
|
|
140
|
+
|
|
141
|
+
details = stderr.to_s.strip
|
|
142
|
+
details = "op exited #{status.exitstatus}" if details.empty?
|
|
143
|
+
raise Kettle::Dev::Error, "1Password #{purpose} lookup failed: #{details}"
|
|
144
|
+
rescue Errno::ENOENT
|
|
145
|
+
raise Kettle::Dev::Error, "1Password CLI executable #{argv.first.inspect} was not found"
|
|
146
|
+
end
|
|
147
|
+
|
|
128
148
|
def required_config(key)
|
|
129
149
|
value = string_config(key)
|
|
130
150
|
raise Kettle::Dev::Error, "1Password release secrets require #{key}" if value.empty?
|
data/lib/kettle/dev/version.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Kettle
|
|
|
5
5
|
# Version namespace for this gem.
|
|
6
6
|
module Version
|
|
7
7
|
# Current gem version.
|
|
8
|
-
VERSION = "3.0.
|
|
8
|
+
VERSION = "3.0.1"
|
|
9
9
|
end
|
|
10
10
|
# Current gem version exposed at the traditional constant location.
|
|
11
11
|
VERSION = Version::VERSION # Traditional Constant Location
|
data.tar.gz.sig
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
�
|
|
1
|
+
S+|I"{w��Vtnhl4eL;�c��X�FwH�(Y�2C�1[���{��[���!����C�䄮N�\��;��^DZ)_��q�[��mSP8��di�U�Z�^GG6F|�M������]�]%,��T�I*�@��"xЂ<�dV��k��Ngv�H'l�闞�Q�F��ə?ȬU0z(�O@
|
|
2
|
+
�R�E+��q�s�U�[���?�c5ɰ1N-
|
|
3
|
+
-��A���i#;'�k�f���5�h�B���-��� ��<���rp���������xj<g�X�+W��4dt4l�����b�X�Y ��\��m�"b%N�z{ ��grY� �]�`��1�n�z�p$+��
|
|
4
|
+
�T�\���B��L!;��Z��<a��]J�y�M�k���j�
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kettle-dev
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter H. Boling
|
|
@@ -400,10 +400,10 @@ licenses:
|
|
|
400
400
|
- AGPL-3.0-only
|
|
401
401
|
metadata:
|
|
402
402
|
homepage_uri: https://kettle-dev.galtzo.com
|
|
403
|
-
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.0.
|
|
404
|
-
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.0.
|
|
403
|
+
source_code_uri: https://github.com/kettle-dev/kettle-dev/tree/v3.0.1
|
|
404
|
+
changelog_uri: https://github.com/kettle-dev/kettle-dev/blob/v3.0.1/CHANGELOG.md
|
|
405
405
|
bug_tracker_uri: https://github.com/kettle-dev/kettle-dev/issues
|
|
406
|
-
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.0.
|
|
406
|
+
documentation_uri: https://www.rubydoc.info/gems/kettle-dev/3.0.1
|
|
407
407
|
funding_uri: https://github.com/sponsors/pboling
|
|
408
408
|
wiki_uri: https://github.com/kettle-dev/kettle-dev/wiki
|
|
409
409
|
news_uri: https://www.railsbling.com/tags/kettle-dev
|
metadata.gz.sig
CHANGED
|
Binary file
|