kettle-family 0.1.3 → 0.1.4

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: 2db11fe0f0487d5a8d9b7413b680997f63b65ba2e025e5b6dbeef9e2a4f0b6c8
4
- data.tar.gz: fcd754360608f5791e7c3372f37cd9e8a073ce1f9a8b1c883e457740d2bf12d5
3
+ metadata.gz: b3ec93e3425d765de198975bb390031d33f4a408c31994a18999e3178aec2cee
4
+ data.tar.gz: c22c0cec6c2dc9a387281adb456d7303bc838e687be27b0c69ecc59b7f8038ae
5
5
  SHA512:
6
- metadata.gz: 8a92079c7ba5f0b0ce6d6cae83ee1625c592270260079643e6f988b3a8e95f839173e6b70c21326eae93a3e1888c49b59f402fdbae12a295a7e903f0e1f2c790
7
- data.tar.gz: 56112e0a206756aa3c9b885029d5ed5c1a555cde829ea56cfbc335df405c3c9bfa54badfc29eebf76b9be339ef55b5999c0250e4db14aa755157de3ff0de2195
6
+ metadata.gz: 7520e01212b4faebaed857c69a7bd0c068be891e8fa79c4ccb7485f9cd8fdd4daa824c5943cc030da765992422a1bf252fe76fde2e68b3b4f0feeaf179beb30a
7
+ data.tar.gz: '0683aae4dfdbbde5b004719856f32b65f7810acd81446145456a7810e4625f85a516f1d53d08d0c1622f65d7d8a2902c0dfb2ff57b35950f19bc76355cdded56'
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,24 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [0.1.4] - 2026-06-16
34
+
35
+ - TAG: [v0.1.4][0.1.4t]
36
+ - COVERAGE: 93.72% -- 1060/1131 lines in 19 files
37
+ - BRANCH COVERAGE: 76.12% -- 322/423 branches in 19 files
38
+ - 40.14% documented
39
+
40
+ ### Added
41
+
42
+ - Added configurable readiness checks, root/shared changelog support, release
43
+ environment overrides, and an optional family changelog release phase for
44
+ monorepo gem families whose members share root release metadata.
45
+
46
+ ### Fixed
47
+
48
+ - Fixed the Ruby 3.2 CI appraisal so root changelog release-state checks have
49
+ Prism available.
50
+
33
51
  ## [0.1.3] - 2026-06-14
34
52
 
35
53
  - TAG: [v0.1.3][0.1.3t]
@@ -122,7 +140,9 @@ Please file a bug if you notice a violation of semantic versioning.
122
140
  - Fixed CI load failures on engines without compatible `pty` support by falling back to Open3 for interactive release commands.
123
141
  - Fixed Ruby 3.2 version-bump support by loading Prism lazily and wiring the Prism gem only for MRI versions that need it.
124
142
 
125
- [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v0.1.3...HEAD
143
+ [Unreleased]: https://github.com/kettle-dev/kettle-family/compare/v0.1.4...HEAD
144
+ [0.1.4]: https://github.com/kettle-dev/kettle-family/compare/v0.1.3...v0.1.4
145
+ [0.1.4t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.1.4
126
146
  [0.1.3]: https://github.com/kettle-dev/kettle-family/compare/v0.1.2...v0.1.3
127
147
  [0.1.3t]: https://github.com/kettle-dev/kettle-family/releases/tag/v0.1.3
128
148
  [0.1.2]: https://github.com/kettle-dev/kettle-family/compare/v0.1.1...v0.1.2
data/CONTRIBUTING.md CHANGED
@@ -109,14 +109,14 @@ Git diff driver setup
109
109
  - Git hosting forges generally ignore external diff drivers, so pull request views may still show raw textual diffs even when local `git diff` uses semantic drivers.
110
110
 
111
111
  ```console
112
- K_JEM_TEMPLATING=true bundle exec kettle-jem install
112
+ K_JEM_TEMPLATING=true kettle-jem install
113
113
  ```
114
114
 
115
115
  Troubleshooting Git diffs
116
116
  - Use `git diff --no-ext-diff` to compare against Git's built-in diff output.
117
117
  - Use `git diff --no-textconv` when a textconv projection obscures the raw file bytes you need to inspect.
118
118
  - If Git reports a missing `smorg-*` executable, rerun `bundle install` and the setup command above, then check `git config --local --get-regexp '^diff\.smorg-'`.
119
- - To remove managed local entries, run `K_JEM_TEMPLATING=true bundle exec kettle-jem install --undo`; remove global command registrations with `git config --global --unset-all diff.smorg-ruby.command`.
119
+ - To remove managed local entries, run `K_JEM_TEMPLATING=true kettle-jem install --undo`; remove global command registrations with `git config --global --unset-all diff.smorg-ruby.command`.
120
120
 
121
121
  For a quick starting point, this repository’s `mise.toml` defines the shared defaults, and `.env.local` can override them locally. Copy `.env.local.example` to `.env.local`, use `KEY=value` lines, and either activate `mise` in your shell or run commands through `mise exec -C /path/to/project -- ...`.
122
122
 
data/README.md CHANGED
@@ -142,6 +142,36 @@ members:
142
142
  - "**/vendor/**"
143
143
  ```
144
144
 
145
+ Monorepo families whose member gems share release metadata from the repository
146
+ root can configure readiness and changelog ownership explicitly:
147
+
148
+ ```yaml
149
+ check:
150
+ required_files:
151
+ - Gemfile
152
+ - Rakefile
153
+ - README.md
154
+ - LICENSE.md
155
+ required_bins:
156
+ - bin/rake
157
+ - bin/rspec
158
+ root_required_files:
159
+ - CHANGELOG.md
160
+ - SECURITY.md
161
+
162
+ changelog:
163
+ mode: root
164
+ path: CHANGELOG.md
165
+ version_file: gems/tree_haver/lib/tree_haver/version.rb
166
+
167
+ release:
168
+ env:
169
+ KETTLE_RB_DEV: false
170
+ family_changelog:
171
+ enabled: true
172
+ command: bundle exec kettle-changelog
173
+ ```
174
+
145
175
  For a flat repository that releases from multiple long-lived branches, list the
146
176
  release branches under `release.target_branches`. The branch list is processed
147
177
  in order. Each branch must be clean enough for `git checkout`, and each branch
@@ -516,7 +546,7 @@ Thanks for RTFM. ☺️
516
546
  [📌gitmoji]: https://gitmoji.dev
517
547
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
518
548
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
519
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-0.972-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
549
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-1.131-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
520
550
  [🔐security]: https://github.com/kettle-dev/kettle-family/blob/main/SECURITY.md
521
551
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
522
552
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
@@ -544,7 +574,7 @@ Thanks for RTFM. ☺️
544
574
  | Package | kettle-family |
545
575
  | Description | 👩‍👩‍👧‍👧 Kettle::Family provides scripts and conventions for coordinating related Ruby gems as one family. |
546
576
  | Homepage | https://github.com/kettle-dev/kettle-family |
547
- | Source | https://github.com/kettle-dev/kettle-family/tree/v0.1.3 |
577
+ | Source | https://github.com/kettle-dev/kettle-family |
548
578
  | License | `AGPL-3.0-only` |
549
579
  | Funding | https://github.com/sponsors/pboling, https://issuehunt.io/u/pboling, https://ko-fi.com/pboling, https://liberapay.com/pboling/donate, https://opencollective.com/kettle-dev, https://opencollective.com/kettle-rb, https://patreon.com/galtzo, https://polar.sh/pboling, https://thanks.dev/u/gh/pboling, https://tidelift.com/funding/github/rubygems/kettle-family, https://www.buymeacoffee.com/pboling |
550
580
  <!-- kettle-jem:metadata:end -->
@@ -3,25 +3,37 @@
3
3
  module Kettle
4
4
  module Family
5
5
  class ChangelogCheck
6
- def self.call(member:)
7
- new(member: member).call
6
+ def self.call(member:, config: nil)
7
+ new(member: member, config: config).call
8
8
  end
9
9
 
10
- def initialize(member:)
10
+ def initialize(member:, config: nil)
11
11
  @member = member
12
+ @config = config
12
13
  end
13
14
 
14
15
  def call
15
16
  diagnostics = []
16
- changelog = File.join(member.root, "CHANGELOG.md")
17
- diagnostics << "missing CHANGELOG.md" unless File.file?(changelog)
18
- diagnostics << "CHANGELOG.md missing Unreleased section" if File.file?(changelog) && !File.read(changelog).include?("## [Unreleased]")
17
+ changelog = changelog_path
18
+ diagnostics << "missing #{relative_changelog_path}" unless File.file?(changelog)
19
+ diagnostics << "#{relative_changelog_path} missing Unreleased section" if File.file?(changelog) && !File.read(changelog).include?("## [Unreleased]")
19
20
  result(diagnostics)
20
21
  end
21
22
 
22
23
  private
23
24
 
24
- attr_reader :member
25
+ attr_reader :member, :config
26
+
27
+ def changelog_path
28
+ config ? config.changelog_full_path(member) : File.join(member.root, "CHANGELOG.md")
29
+ end
30
+
31
+ def relative_changelog_path
32
+ return "CHANGELOG.md" unless config
33
+
34
+ base = config.shared_changelog? ? config.root : member.root
35
+ changelog_path.delete_prefix("#{base}/")
36
+ end
25
37
 
26
38
  def result(diagnostics)
27
39
  CommandResult.new(
@@ -90,6 +90,64 @@ module Kettle
90
90
  fetch_path("commands", name)
91
91
  end
92
92
 
93
+ def check_required_files
94
+ fetch_path("check", "required_files") || ReadinessCheck::REQUIRED_FILES
95
+ end
96
+
97
+ def check_required_bins
98
+ fetch_path("check", "required_bins") || ReadinessCheck::REQUIRED_BINS
99
+ end
100
+
101
+ def check_root_required_files
102
+ fetch_path("check", "root_required_files") || []
103
+ end
104
+
105
+ def check_member_required_dirs
106
+ fetch_path("check", "member_required_dirs") || []
107
+ end
108
+
109
+ def check_forbidden_tracked_member_dirs
110
+ fetch_path("check", "forbidden_tracked_member_dirs") || []
111
+ end
112
+
113
+ def check_forbidden_tracked_member_dirs_except
114
+ fetch_path("check", "forbidden_tracked_member_dirs_except") || []
115
+ end
116
+
117
+ def check_readme_links
118
+ fetch_path("check", "readme_links") || {}
119
+ end
120
+
121
+ def changelog_mode
122
+ fetch_path("changelog", "mode") || "member"
123
+ end
124
+
125
+ def shared_changelog?
126
+ changelog_mode == "root"
127
+ end
128
+
129
+ def changelog_path
130
+ fetch_path("changelog", "path") || "CHANGELOG.md"
131
+ end
132
+
133
+ def changelog_version_file
134
+ fetch_path("changelog", "version_file")
135
+ end
136
+
137
+ def changelog_workdir(_member = nil)
138
+ shared_changelog? ? root : nil
139
+ end
140
+
141
+ def changelog_full_path(member)
142
+ File.expand_path(changelog_path, shared_changelog? ? root : member.root)
143
+ end
144
+
145
+ def changelog_env
146
+ return {} unless changelog_version_file
147
+
148
+ {"K_CHANGELOG_VERSION_FILE" => changelog_version_file.to_s}
149
+ end
150
+
93
151
  def template_command
94
152
  fetch_path("template", "command") || command_for("template")
95
153
  end
@@ -118,6 +176,18 @@ module Kettle
118
176
  fetch_path("release", "publish_command") || command_for("release_publish") || "bundle exec kettle-release"
119
177
  end
120
178
 
179
+ def release_env
180
+ stringify_env(fetch_path("release", "env") || {})
181
+ end
182
+
183
+ def release_family_changelog?
184
+ fetch_path("release", "family_changelog", "enabled") == true
185
+ end
186
+
187
+ def release_family_changelog_command
188
+ fetch_path("release", "family_changelog", "command") || "bundle exec kettle-changelog"
189
+ end
190
+
121
191
  def release_tag_command
122
192
  fetch_path("release", "tag_command") || command_for("release_tag") || "git tag"
123
193
  end
@@ -160,6 +230,10 @@ module Kettle
160
230
  value
161
231
  end
162
232
  end
233
+
234
+ def stringify_env(value)
235
+ stringify_keys(value).to_h { |key, item| [key.to_s, item.to_s] }
236
+ end
163
237
  end
164
238
  end
165
239
  end
@@ -6,28 +6,33 @@ module Kettle
6
6
  REQUIRED_FILES = %w[Gemfile Rakefile README.md CHANGELOG.md LICENSE.md].freeze
7
7
  REQUIRED_BINS = %w[bin/rake bin/rspec].freeze
8
8
 
9
- def self.call(member:)
10
- new(member: member).call
9
+ def self.call(member:, config: nil)
10
+ new(member: member, config: config).call
11
11
  end
12
12
 
13
- def initialize(member:)
13
+ def initialize(member:, config: nil)
14
14
  @member = member
15
+ @config = config
15
16
  end
16
17
 
17
18
  def call
18
19
  diagnostics = []
19
20
  diagnostics.concat(missing_required_files)
20
21
  diagnostics.concat(missing_required_bins)
22
+ diagnostics.concat(missing_root_required_files)
23
+ diagnostics.concat(missing_member_required_dirs)
24
+ diagnostics.concat(forbidden_tracked_member_dirs)
25
+ diagnostics.concat(missing_readme_links)
21
26
  diagnostics.concat(local_path_lockfile_entries)
22
27
  result(diagnostics)
23
28
  end
24
29
 
25
30
  private
26
31
 
27
- attr_reader :member
32
+ attr_reader :member, :config
28
33
 
29
34
  def missing_required_files
30
- REQUIRED_FILES.filter_map do |path|
35
+ required_files.filter_map do |path|
31
36
  next if File.file?(File.join(member.root, path))
32
37
 
33
38
  "missing required file #{path}"
@@ -35,7 +40,7 @@ module Kettle
35
40
  end
36
41
 
37
42
  def missing_required_bins
38
- REQUIRED_BINS.filter_map do |path|
43
+ required_bins.filter_map do |path|
39
44
  full_path = File.join(member.root, path)
40
45
  next if File.file?(full_path) && File.executable?(full_path)
41
46
 
@@ -43,6 +48,52 @@ module Kettle
43
48
  end
44
49
  end
45
50
 
51
+ def missing_root_required_files
52
+ return [] unless config
53
+
54
+ config.check_root_required_files.filter_map do |path|
55
+ next if File.file?(File.join(config.root, path))
56
+
57
+ "missing root required file #{path}"
58
+ end
59
+ end
60
+
61
+ def missing_member_required_dirs
62
+ return [] unless config
63
+
64
+ config.check_member_required_dirs.filter_map do |path|
65
+ next if Dir.exist?(File.join(member.root, path))
66
+
67
+ "missing required directory #{path}"
68
+ end
69
+ end
70
+
71
+ def forbidden_tracked_member_dirs
72
+ return [] unless config
73
+ return [] if config.check_forbidden_tracked_member_dirs_except.include?(member.name)
74
+
75
+ config.check_forbidden_tracked_member_dirs.filter_map do |path|
76
+ full_path = File.join(member.root, path)
77
+ next unless Dir.exist?(full_path) && tracked_path?(full_path)
78
+
79
+ "forbidden tracked directory #{path}"
80
+ end
81
+ end
82
+
83
+ def missing_readme_links
84
+ return [] unless config
85
+
86
+ readme = File.join(member.root, "README.md")
87
+ return [] unless File.file?(readme)
88
+
89
+ content = File.read(readme)
90
+ config.check_readme_links.filter_map do |label, target|
91
+ next if content.include?("/#{target}") || content.include?("../../#{target}") || content.include?("../#{target}")
92
+
93
+ "README.md missing link to root #{label}"
94
+ end
95
+ end
96
+
46
97
  def local_path_lockfile_entries
47
98
  lockfile = File.join(member.root, "Gemfile.lock")
48
99
  return [] unless File.file?(lockfile)
@@ -54,6 +105,21 @@ module Kettle
54
105
  end
55
106
  end
56
107
 
108
+ def required_files
109
+ config ? config.check_required_files : REQUIRED_FILES
110
+ end
111
+
112
+ def required_bins
113
+ config ? config.check_required_bins : REQUIRED_BINS
114
+ end
115
+
116
+ def tracked_path?(path)
117
+ return false unless config
118
+
119
+ relative = path.delete_prefix("#{config.root}/")
120
+ system("git", "-C", config.root, "ls-files", "--error-unmatch", relative, out: File::NULL, err: File::NULL)
121
+ end
122
+
57
123
  def result(diagnostics)
58
124
  CommandResult.new(
59
125
  member_name: member.name,
@@ -16,6 +16,7 @@ module Kettle
16
16
 
17
17
  def results
18
18
  return branch_results unless release_target_branches.empty?
19
+ return [check_family_changelog] if shared_changelog?
19
20
 
20
21
  members.map { |member| check_member(member) }
21
22
  end
@@ -29,6 +30,11 @@ module Kettle
29
30
  selected_names = members.map(&:name)
30
31
  release_target_branches.each_with_object([]) do |branch, memo|
31
32
  with_branch_worktree(root: root, branch: branch) do |worktree_root|
33
+ if shared_changelog?
34
+ memo << check_family_changelog(branch: branch, worktree_root: worktree_root)
35
+ next
36
+ end
37
+
32
38
  branch_members = discover_branch_members(worktree_root: worktree_root, selected_names: selected_names)
33
39
  memo.concat(branch_members.map { |member| check_member(member, branch: branch) })
34
40
  end
@@ -40,7 +46,7 @@ module Kettle
40
46
  def check_member(member, branch: nil)
41
47
  started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
42
48
  command = release_state_command
43
- stdout, stderr, status = Open3.capture3(*command, chdir: member.root)
49
+ stdout, stderr, status = Open3.capture3(release_state_env, *command, chdir: release_state_workdir(member))
44
50
  elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
45
51
  success = status.success?
46
52
  state = success ? JSON.parse(stdout) : {}
@@ -53,6 +59,143 @@ module Kettle
53
59
  [RbConfig.ruby, "-S", "kettle-changelog", "--release-state", "--json"]
54
60
  end
55
61
 
62
+ def check_family_changelog(branch: nil, worktree_root: nil)
63
+ member = family_member(root: worktree_root ? branch_config_root(worktree_root) : config.root)
64
+ started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
65
+ state = family_changelog_state(member.root)
66
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started
67
+ result(
68
+ member: member,
69
+ command: ["internal", "release-state", "root-changelog"],
70
+ stdout: "",
71
+ stderr: "",
72
+ status: 0,
73
+ elapsed: elapsed,
74
+ success: true,
75
+ state: state,
76
+ branch: branch
77
+ )
78
+ rescue Error => error
79
+ result(
80
+ member: member,
81
+ command: ["internal", "release-state", "root-changelog"],
82
+ stdout: "",
83
+ stderr: error.message,
84
+ status: 1,
85
+ elapsed: 0.0,
86
+ success: false,
87
+ state: {},
88
+ reason: "release state check failed",
89
+ branch: branch
90
+ )
91
+ end
92
+
93
+ def family_member(root:)
94
+ Member.new(
95
+ name: config.family_name,
96
+ root: root,
97
+ gemspec_path: nil,
98
+ version_file: nil,
99
+ version: nil,
100
+ dependencies: []
101
+ )
102
+ end
103
+
104
+ def release_state_workdir(member)
105
+ return member.root unless config
106
+ return member.root if config.shared_changelog?
107
+
108
+ config.changelog_workdir(member) || member.root
109
+ end
110
+
111
+ def release_state_env
112
+ config ? config.changelog_env : {}
113
+ end
114
+
115
+ def family_changelog_state(root)
116
+ changelog = File.expand_path(config.changelog_path, root)
117
+ raise Error, "missing root changelog #{config.changelog_path}" unless File.file?(changelog)
118
+
119
+ version = root_changelog_version(root)
120
+ content = File.read(changelog)
121
+ latest_changelog_version = latest_changelog_version(content)
122
+ unreleased_entries = unreleased_entries?(content)
123
+ prepared_release_pending = !version.to_s.empty? && latest_changelog_version == version
124
+ {
125
+ "gem_name" => config.family_name,
126
+ "version" => version,
127
+ "latest_released" => nil,
128
+ "latest_changelog_version" => latest_changelog_version,
129
+ "unreleased_entries" => unreleased_entries,
130
+ "prepared_release_pending" => prepared_release_pending,
131
+ "pending_release" => unreleased_entries || prepared_release_pending
132
+ }
133
+ end
134
+
135
+ def root_changelog_version(root)
136
+ version_file = config.changelog_version_file
137
+ return nil unless version_file
138
+
139
+ path = File.expand_path(version_file, root)
140
+ raise Error, "missing changelog version file #{version_file}" unless File.file?(path)
141
+
142
+ version_string_node(File.read(path), path).unescaped
143
+ end
144
+
145
+ def version_string_node(source, path)
146
+ require_prism
147
+ parse_result = Prism.parse(source)
148
+ raise Error, "could not parse #{path}" unless parse_result.success?
149
+
150
+ constant = each_node(parse_result.value).find do |node|
151
+ node.is_a?(Prism::ConstantWriteNode) && node.name == :VERSION && node.value.is_a?(Prism::StringNode)
152
+ end
153
+ raise Error, "could not find string VERSION constant in #{path}" unless constant
154
+
155
+ constant.value
156
+ end
157
+
158
+ def latest_changelog_version(content)
159
+ content.each_line.filter_map do |line|
160
+ match = line.match(/\A## \[([^\]]+)\]/)
161
+ next unless match
162
+
163
+ version = match[1]
164
+ next if version == "Unreleased"
165
+
166
+ version
167
+ end.first
168
+ end
169
+
170
+ def unreleased_entries?(content)
171
+ lines = content.lines
172
+ start = lines.index { |line| line.start_with?("## [Unreleased]") }
173
+ return false unless start
174
+
175
+ following = lines[(start + 1)..] || []
176
+ block = following.take_while { |line| !line.start_with?("## [") }
177
+ block.any? { |line| line.match?(/\S/) && !line.match?(/\A###? /) }
178
+ end
179
+
180
+ def require_prism
181
+ return if defined?(Prism)
182
+
183
+ require "prism"
184
+ rescue LoadError => error
185
+ raise Error, "root changelog release-state requires Prism; install the prism gem or run on a Ruby engine that provides it (#{error.message})"
186
+ end
187
+
188
+ def each_node(root)
189
+ return enum_for(__method__, root) unless block_given?
190
+
191
+ queue = [root]
192
+ until queue.empty?
193
+ node = queue.shift
194
+ yield node
195
+ queue.concat(node.child_nodes.compact) if node.respond_to?(:child_nodes)
196
+ end
197
+ end
198
+
56
199
  def result(member:, command:, stdout:, stderr:, status:, elapsed:, success:, state:, reason: nil, branch: nil)
57
200
  ReleaseStateResult.new(
58
201
  member_name: member.name,
@@ -91,6 +234,10 @@ module Kettle
91
234
  config.release_target_branches
92
235
  end
93
236
 
237
+ def shared_changelog?
238
+ config&.shared_changelog?
239
+ end
240
+
94
241
  def git_root
95
242
  stdout, stderr, status = Open3.capture3("git", "rev-parse", "--show-toplevel", chdir: config.root)
96
243
  raise Error, "could not determine git root for #{config.root}: #{stderr}" unless status.success?
@@ -3,7 +3,7 @@
3
3
  module Kettle
4
4
  module Family
5
5
  module Version
6
- VERSION = "0.1.3"
6
+ VERSION = "0.1.4"
7
7
  end
8
8
  VERSION = Version::VERSION # Traditional Constant Location
9
9
  end
@@ -54,14 +54,14 @@ module Kettle
54
54
  attr_reader :command, :config, :members, :execute, :commit, :allow_dirty, :publish, :push, :tag, :start_step, :local_ci, :continue_ci_failures
55
55
 
56
56
  def check_results
57
- members.map { |member| ReadinessCheck.call(member: member) }
57
+ members.map { |member| ReadinessCheck.call(member: member, config: config) }
58
58
  end
59
59
 
60
60
  def release_results
61
61
  prompt_for_gem_signing_password if execute && publish && gem_signing_required?
62
62
  return branch_target_release_results unless config.release_target_branches.empty?
63
63
 
64
- release_member_results(members)
64
+ release_member_results(members, include_family_changelog: true)
65
65
  end
66
66
 
67
67
  def branch_target_release_results
@@ -72,14 +72,18 @@ module Kettle
72
72
  break memo unless memo.last.ok?
73
73
 
74
74
  branch_members = rediscovered_selected_members(selected_names)
75
- memo.concat(release_member_results(branch_members))
75
+ memo.concat(release_member_results(branch_members, include_family_changelog: true))
76
76
  break memo unless memo.last&.ok?
77
77
  end
78
78
  end
79
79
 
80
- def release_member_results(release_members)
80
+ def release_member_results(release_members, include_family_changelog: false)
81
81
  runner = command_runner
82
- release_members.each_with_object([]) do |member, memo|
82
+ results = []
83
+ append_family_changelog_result(runner: runner, memo: results) if include_family_changelog
84
+ return results unless results.all?(&:ok?)
85
+
86
+ release_members.each_with_object(results) do |member, memo|
83
87
  if skip_already_released?(member)
84
88
  memo << already_released_result(member)
85
89
  next
@@ -121,8 +125,19 @@ module Kettle
121
125
  end
122
126
 
123
127
  def append_release_internal_checks(member:, memo:)
124
- memo << ReadinessCheck.call(member: member)
125
- memo << ChangelogCheck.call(member: member) if memo.last.ok?
128
+ memo << ReadinessCheck.call(member: member, config: config)
129
+ memo << ChangelogCheck.call(member: member, config: config) if memo.last.ok?
130
+ end
131
+
132
+ def append_family_changelog_result(runner:, memo:)
133
+ return unless config.release_family_changelog?
134
+
135
+ memo << runner.call(
136
+ member: family_member,
137
+ phase: "family_changelog",
138
+ command: config.release_family_changelog_command,
139
+ env: release_env.merge(config.changelog_env)
140
+ )
126
141
  end
127
142
 
128
143
  def release_phase
@@ -155,7 +170,9 @@ module Kettle
155
170
  end
156
171
 
157
172
  def release_env
158
- continue_ci_failures ? {"K_RELEASE_CI_CONTINUE" => "true"} : {}
173
+ env = config.release_env.dup
174
+ env["K_RELEASE_CI_CONTINUE"] = "true" if continue_ci_failures
175
+ env
159
176
  end
160
177
 
161
178
  def append_release_git_phases(member:, runner:, memo:)
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-family
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -60,7 +60,7 @@ dependencies:
60
60
  version: '1.1'
61
61
  - - ">="
62
62
  - !ruby/object:Gem::Version
63
- version: 1.1.11
63
+ version: 1.1.12
64
64
  type: :runtime
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
@@ -70,7 +70,7 @@ dependencies:
70
70
  version: '1.1'
71
71
  - - ">="
72
72
  - !ruby/object:Gem::Version
73
- version: 1.1.11
73
+ version: 1.1.12
74
74
  - !ruby/object:Gem::Dependency
75
75
  name: kettle-dev
76
76
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +80,7 @@ dependencies:
80
80
  version: '2.2'
81
81
  - - ">="
82
82
  - !ruby/object:Gem::Version
83
- version: 2.2.8
83
+ version: 2.2.9
84
84
  type: :development
85
85
  prerelease: false
86
86
  version_requirements: !ruby/object:Gem::Requirement
@@ -90,7 +90,7 @@ dependencies:
90
90
  version: '2.2'
91
91
  - - ">="
92
92
  - !ruby/object:Gem::Version
93
- version: 2.2.8
93
+ version: 2.2.9
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: bundler-audit
96
96
  requirement: !ruby/object:Gem::Requirement
@@ -188,7 +188,7 @@ dependencies:
188
188
  version: '3.1'
189
189
  - - ">="
190
190
  - !ruby/object:Gem::Version
191
- version: 3.1.2
191
+ version: 3.1.3
192
192
  type: :development
193
193
  prerelease: false
194
194
  version_requirements: !ruby/object:Gem::Requirement
@@ -198,7 +198,7 @@ dependencies:
198
198
  version: '3.1'
199
199
  - - ">="
200
200
  - !ruby/object:Gem::Version
201
- version: 3.1.2
201
+ version: 3.1.3
202
202
  - !ruby/object:Gem::Dependency
203
203
  name: ruby-progressbar
204
204
  requirement: !ruby/object:Gem::Requirement
@@ -242,7 +242,7 @@ dependencies:
242
242
  version: '2.0'
243
243
  - - ">="
244
244
  - !ruby/object:Gem::Version
245
- version: 2.0.1
245
+ version: 2.0.2
246
246
  type: :development
247
247
  prerelease: false
248
248
  version_requirements: !ruby/object:Gem::Requirement
@@ -252,7 +252,7 @@ dependencies:
252
252
  version: '2.0'
253
253
  - - ">="
254
254
  - !ruby/object:Gem::Version
255
- version: 2.0.1
255
+ version: 2.0.2
256
256
  description: "\U0001F469‍\U0001F469‍\U0001F467‍\U0001F467 Kettle::Family provides
257
257
  scripts and conventions for coordinating related Ruby gems as one family."
258
258
  email:
@@ -308,10 +308,10 @@ licenses:
308
308
  - AGPL-3.0-only
309
309
  metadata:
310
310
  homepage_uri: https://kettle-family.galtzo.com
311
- source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v0.1.3
312
- changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v0.1.3/CHANGELOG.md
311
+ source_code_uri: https://github.com/kettle-dev/kettle-family/tree/v0.1.4
312
+ changelog_uri: https://github.com/kettle-dev/kettle-family/blob/v0.1.4/CHANGELOG.md
313
313
  bug_tracker_uri: https://github.com/kettle-dev/kettle-family/issues
314
- documentation_uri: https://www.rubydoc.info/gems/kettle-family/0.1.3
314
+ documentation_uri: https://www.rubydoc.info/gems/kettle-family/0.1.4
315
315
  funding_uri: https://github.com/sponsors/pboling
316
316
  wiki_uri: https://github.com/kettle-dev/kettle-family/wiki
317
317
  news_uri: https://www.railsbling.com/tags/kettle-family
metadata.gz.sig CHANGED
Binary file