kettle-changelog 1.0.5 → 1.0.7

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: 987f0528fbdee3c91c47d9675fe79fe3e4993f69c4b759ee7413a07c0b6eece9
4
- data.tar.gz: d20d9e1fff2153b932cb3961d8bb56fd8bd02b0e9257b4b2844eaaa70ac7cb11
3
+ metadata.gz: 2ecd249c6b061cc317b81a854a766b92f0f8563ec986d46ba301c91aa0e03be9
4
+ data.tar.gz: 5aa44defac27dbce2d14a0b52f24647a700f9910980ab5dea0ed194586c2bb26
5
5
  SHA512:
6
- metadata.gz: d379f0164efc1c4d7214607eb2e745895dce3cb5e0702da2d27d63f62f376ec077b842061e74d98a93ca4ad9015cc3200e4daf8ca73a3d9ba338e8bbb339fc61
7
- data.tar.gz: 21ecb7793e5b0ebc36d2e0987419a3b9f900267ce04e1edabdfa18c5c3f3e72bc112d6456a95bdc2b8f79df77dc7cf58cd7b2057049aa90238ebd501d1796a8d
6
+ metadata.gz: 4f2695df4b42baf349133ce330e7e35ea80ec0060a7d50e6373249717cd171fdbbc50898f89dfdbfc2e5fab4d698bcc60b30512f170d0bccbd9bf33a5cd3c7d9
7
+ data.tar.gz: 85cc9764d62f271e046efec6730b491c0a124d78a52dea7732f4fd31f94ebb4bd2e2b8d994147d51b1fe92ccfdd20d3289cad70681ca1883a65d2a11ba3154c5
checksums.yaml.gz.sig CHANGED
Binary file
data/CHANGELOG.md CHANGED
@@ -30,6 +30,32 @@ Please file a bug if you notice a violation of semantic versioning.
30
30
 
31
31
  ### Security
32
32
 
33
+ ## [1.0.7] - 2026-08-30
34
+
35
+ - TAG: [v1.0.7][1.0.7t]
36
+ - COVERAGE: 94.35% -- 1136/1204 lines in 6 files
37
+ - BRANCH COVERAGE: 77.73% -- 398/512 branches in 6 files
38
+ - 38.89% documented
39
+
40
+ ### Fixed
41
+
42
+ - Honor the release-provided disposable lockfile when generating strict changelog coverage.
43
+
44
+ - Use generated coverage bundles for changelog coverage when available, with explicit bundle selection overrides.
45
+
46
+ ## [1.0.6] - 2026-08-29
47
+
48
+ - TAG: [v1.0.6][1.0.6t]
49
+ - COVERAGE: 94.27% -- 1119/1187 lines in 6 files
50
+ - BRANCH COVERAGE: 77.31% -- 385/498 branches in 6 files
51
+ - 38.89% documented
52
+
53
+ ### Fixed
54
+
55
+ - Consolidate repeated keyed project-file summaries when updating a prepared release section.
56
+
57
+ - Consolidate repeated keyed project-file summaries within each published release during changelog reformatting.
58
+
33
59
  ## [1.0.5] - 2026-08-28
34
60
 
35
61
  - TAG: [v1.0.5][1.0.5t]
@@ -179,7 +205,11 @@ Please file a bug if you notice a violation of semantic versioning.
179
205
 
180
206
  - Initial release
181
207
 
182
- [Unreleased]: https://github.com/kettle-dev/kettle-changelog/compare/v1.0.5...HEAD
208
+ [Unreleased]: https://github.com/kettle-dev/kettle-changelog/compare/v1.0.7...HEAD
209
+ [1.0.7]: https://github.com/kettle-dev/kettle-changelog/compare/v1.0.6...v1.0.7
210
+ [1.0.7t]: https://github.com/kettle-dev/kettle-changelog/releases/tag/v1.0.7
211
+ [1.0.6]: https://github.com/kettle-dev/kettle-changelog/compare/v1.0.5...v1.0.6
212
+ [1.0.6t]: https://github.com/kettle-dev/kettle-changelog/releases/tag/v1.0.6
183
213
  [1.0.5]: https://github.com/kettle-dev/kettle-changelog/compare/v1.0.4...v1.0.5
184
214
  [1.0.5t]: https://github.com/kettle-dev/kettle-changelog/releases/tag/v1.0.5
185
215
  [1.0.4]: https://github.com/kettle-dev/kettle-changelog/compare/v1.0.3...v1.0.4
data/README.md CHANGED
@@ -123,6 +123,7 @@ gem install kettle-changelog
123
123
 
124
124
  - `K_CHANGELOG_PATH` selects a changelog outside the current project root.
125
125
  - `K_CHANGELOG_COVERAGE_ROOT` selects the project root used for coverage generation.
126
+ - `K_CHANGELOG_COVERAGE_GEMFILE` selects the target-project Gemfile used for coverage generation. It is resolved relative to the coverage root; absent an explicit setting, `gemfiles/coverage.gemfile` is preferred when generated by Appraisal, then the root `Gemfile` is used.
126
127
  - `K_CHANGELOG_VERSION_FILE` selects the version file when the project has more than one version tree.
127
128
  - `K_CHANGELOG_GEM_NAME` overrides the gem name used for release lookups.
128
129
  - `K_CHANGELOG_STRICT=false` allows missing coverage or YARD data.
@@ -137,6 +138,10 @@ Run the release preparation command from the gem root:
137
138
  bundle exec kettle-changelog
138
139
  ```
139
140
 
141
+ Use `--coverage-gemfile PATH` to select a different target-project coverage
142
+ bundle for one invocation. This is intentionally separate from `BUNDLE_GEMFILE`,
143
+ which may be required to run `kettle-changelog` itself from a release bundle.
144
+
140
145
  Use `--yes` for non-interactive release automation. Add a note without preparing a release with:
141
146
 
142
147
  ```console
@@ -397,7 +402,7 @@ Thanks for RTFM. ☺️
397
402
  [📌gitmoji]: https://gitmoji.dev
398
403
  [📌gitmoji-img]: https://img.shields.io/badge/gitmoji_commits-%20%F0%9F%98%9C%20%F0%9F%98%8D-34495e.svg?style=flat-square
399
404
  [🧮kloc]: https://www.youtube.com/watch?v=dQw4w9WgXcQ
400
- [🧮kloc-img]: https://img.shields.io/badge/KLOC-1.143-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
405
+ [🧮kloc-img]: https://img.shields.io/badge/KLOC-1.204-FFDD67.svg?style=for-the-badge&logo=YouTube&logoColor=blue
401
406
  [🔐security]: https://github.com/kettle-dev/kettle-changelog/blob/main/SECURITY.md
402
407
  [🔐security-img]: https://img.shields.io/badge/security-policy-259D6C.svg?style=flat
403
408
  [📄copyright-notice-explainer]: https://opensource.stackexchange.com/questions/5778/why-do-licenses-such-as-the-mit-license-specify-a-single-year
data/exe/kettle-changelog CHANGED
@@ -63,7 +63,7 @@ end
63
63
 
64
64
  if ARGV.include?("-h") || ARGV.include?("--help")
65
65
  puts <<~USAGE
66
- Usage: kettle-changelog [--version VERSION] [--yes] [--update-prep|--reformat|--backfill-released-version VERSION] [--pending-release] [--release-state] [--refresh-cache] [--add-unreleased-entry --section SECTION --entry TEXT] [--upsert-unreleased-entry --section SECTION --key KEY --entry TEXT] [--json|--events[=TYPES]] [--no-strict] [--no-coverage-threshold]
66
+ Usage: kettle-changelog [--version VERSION] [--yes] [--coverage-gemfile PATH] [--update-prep|--reformat|--backfill-released-version VERSION] [--pending-release] [--release-state] [--refresh-cache] [--add-unreleased-entry --section SECTION --entry TEXT] [--upsert-unreleased-entry --section SECTION --key KEY --entry TEXT] [--json|--events[=TYPES]] [--no-strict] [--no-coverage-threshold]
67
67
 
68
68
  Detects the current version from lib/**/version.rb, the latest live release, and
69
69
  the most recent CHANGELOG.md release section, then prompts to confirm the selected plan:
@@ -93,12 +93,14 @@ if ARGV.include?("-h") || ARGV.include?("--help")
93
93
  --events[=TYPES] Print newline-delimited JSON changelog events
94
94
  --no-strict Allow missing coverage and yard data (warnings only, no errors)
95
95
  --no-coverage-threshold Generate coverage without hard-failing below configured thresholds
96
+ --coverage-gemfile PATH Run coverage through this target-project Gemfile
96
97
 
97
98
  Environment:
98
99
  K_CHANGELOG_STRICT=false Disable strict mode (equivalent to --no-strict flag)
99
100
  K_CHANGELOG_COVERAGE_HARD=false Disable coverage threshold hard-failure
100
101
  K_CHANGELOG_PATH=path Read and write this changelog instead of ./CHANGELOG.md
101
102
  K_CHANGELOG_COVERAGE_ROOT=path Run/read coverage from this root instead of the gem root
103
+ K_CHANGELOG_COVERAGE_GEMFILE=path Target-project Gemfile for coverage; defaults to gemfiles/coverage.gemfile, then Gemfile
102
104
  K_CHANGELOG_VERSION_FILE=path Read VERSION from this file instead of lib/**/version.rb
103
105
  K_CHANGELOG_GEM_NAME=name Read live release data as this gem instead of root .gemspec
104
106
  KETTLE_GEM_COOP_REFRESH=true Bypass cached gem.coop version data
@@ -206,6 +208,7 @@ begin
206
208
  end
207
209
 
208
210
  historical_backfill_version = extract_option_arg!(ARGV, "--backfill-released-version")
211
+ coverage_gemfile = extract_option_arg!(ARGV, "--coverage-gemfile")
209
212
 
210
213
  if upsert_unreleased_entry
211
214
  section = extract_option_arg!(ARGV, "--section")
@@ -240,7 +243,7 @@ begin
240
243
  end
241
244
 
242
245
  event_stream = events_output ? Kettle::Ndjson.event_stream($stdout, types: event_types) : nil
243
- cli = Kettle::Changelog::CLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, reformat_only: reformat_only, historical_backfill_version: historical_backfill_version, version: version_override, refresh_cache: refresh_cache, yes: yes, event_stream: event_stream)
246
+ cli = Kettle::Changelog::CLI.new(strict: strict_mode, enforce_coverage_thresholds: coverage_hard, update_prep: update_prep, reformat_only: reformat_only, historical_backfill_version: historical_backfill_version, version: version_override, refresh_cache: refresh_cache, yes: yes, event_stream: event_stream, coverage_gemfile: coverage_gemfile)
244
247
  if release_state_query
245
248
  state = cli.release_state
246
249
  puts(json_output ? JSON.pretty_generate(state) : cli.release_state_table(state))
@@ -35,10 +35,11 @@ module Kettle
35
35
  # @param historical_backfill_version [String, nil] tagged release to document without moving Unreleased entries
36
36
  # @param version [String, nil] explicit version override for gems without a literal VERSION constant
37
37
  # @param yes [Boolean] when true, approve the selected release plan without prompting
38
- def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, reformat_only: false, historical_backfill_version: nil, version: nil, root: Kettle::Dev::CIHelpers.project_root, refresh_cache: false, yes: false, event_stream: nil)
38
+ def initialize(strict: true, enforce_coverage_thresholds: true, update_prep: false, reformat_only: false, historical_backfill_version: nil, version: nil, root: Kettle::Dev::CIHelpers.project_root, refresh_cache: false, yes: false, event_stream: nil, coverage_gemfile: nil)
39
39
  @root = root
40
40
  @changelog_path = resolved_changelog_path
41
41
  @coverage_root = resolved_coverage_root
42
+ @coverage_gemfile = resolved_coverage_gemfile(coverage_gemfile)
42
43
  @coverage_path = File.join(@coverage_root, "coverage", "coverage.json")
43
44
  @strict = strict
44
45
  @enforce_coverage_thresholds = enforce_coverage_thresholds
@@ -425,6 +426,7 @@ module Kettle
425
426
  updated = convert_heading_tag_suffix_to_list(changelog)
426
427
  updated = normalize_legacy_release_headings(updated)
427
428
  updated = remove_redundant_historical_placeholders(updated)
429
+ updated = collapse_keyed_project_file_updates_by_release(updated)
428
430
  updated = normalize_heading_spacing(updated)
429
431
  updated = ensure_footer_spacing(updated)
430
432
  updated = updated.rstrip + "\n"
@@ -460,6 +462,15 @@ module Kettle
460
462
  lines.each_with_index.reject { |_line, index| removals.any? { |range| range.cover?(index) } }.map(&:first).join
461
463
  end
462
464
 
465
+ def collapse_keyed_project_file_updates_by_release(changelog)
466
+ lines = changelog.to_s.lines
467
+ changelog_release_sections(lines).sort_by { |section| section.fetch(:start) }.reverse_each do |section|
468
+ range = section.fetch(:start)...section.fetch(:finish)
469
+ lines[range] = KeyedEntryUpserter.collapse_project_file_updates(lines[range].join).lines
470
+ end
471
+ lines.join
472
+ end
473
+
463
474
  def changelog_release_sections(lines)
464
475
  headings = lines.each_index.filter_map do |index|
465
476
  match = lines[index].match(/^## \[(#{CHANGELOG_VERSION_PATTERN_SOURCE})\]/o)
@@ -710,7 +721,8 @@ module Kettle
710
721
  end
711
722
  end
712
723
 
713
- ([leading] + sections.map { |section| section.fetch(:heading) + trim_blank_lines(section.fetch(:lines)).join }).join.rstrip + "\n"
724
+ merged = ([leading] + sections.map { |section| section.fetch(:heading) + trim_blank_lines(section.fetch(:lines)).join }).join.rstrip + "\n"
725
+ KeyedEntryUpserter.collapse_project_file_updates(merged)
714
726
  end
715
727
 
716
728
  def strip_release_metadata(release_body)
@@ -928,11 +940,42 @@ module Kettle
928
940
  env[key] = nil
929
941
  end
930
942
  env.merge!(changelog_coverage_workflow_thresholds)
931
- gemfile = File.join(@coverage_root, "Gemfile")
932
- env["BUNDLE_GEMFILE"] = gemfile if File.file?(gemfile)
943
+ env["BUNDLE_GEMFILE"] = @coverage_gemfile if @coverage_gemfile
944
+ coverage_lockfile = ENV.fetch("KETTLE_CHANGELOG_COVERAGE_LOCKFILE", "").to_s.strip
945
+ unless coverage_lockfile.empty?
946
+ raise "Configured changelog coverage lockfile does not exist: #{coverage_lockfile}" unless File.file?(coverage_lockfile)
947
+
948
+ env["BUNDLE_LOCKFILE"] = coverage_lockfile
949
+ end
950
+ # This is a release-tool handoff, not target-project configuration.
951
+ env["KETTLE_CHANGELOG_COVERAGE_LOCKFILE"] = nil
933
952
  env
934
953
  end
935
954
 
955
+ # Coverage has a deliberately smaller bundle than the project's normal
956
+ # development bundle when Appraisal generated gemfiles/coverage.gemfile.
957
+ # Keep the explicit setting separate from BUNDLE_GEMFILE: that variable
958
+ # commonly belongs to the outer kettle-changelog release bundle.
959
+ def resolved_coverage_gemfile(explicit_gemfile)
960
+ requested = explicit_gemfile.to_s.strip
961
+ requested = ENV.fetch("K_CHANGELOG_COVERAGE_GEMFILE", "").to_s.strip if requested.empty?
962
+
963
+ unless requested.empty?
964
+ path = File.expand_path(requested, @coverage_root)
965
+ raise "Configured changelog coverage Gemfile does not exist: #{path}" unless File.file?(path)
966
+
967
+ return path
968
+ end
969
+
970
+ generated = File.join(@coverage_root, "gemfiles", "coverage.gemfile")
971
+ return generated if File.file?(generated)
972
+
973
+ root_gemfile = File.join(@coverage_root, "Gemfile")
974
+ return root_gemfile if File.file?(root_gemfile)
975
+
976
+ nil
977
+ end
978
+
936
979
  # The changelog coverage run is the final local test gate before a
937
980
  # release commit is pushed. Reuse the checked-in CI workflow thresholds
938
981
  # rather than silently falling back to kettle-soup-cover defaults.
@@ -5,6 +5,67 @@ module Kettle
5
5
  class KeyedEntryUpserter
6
6
  SECTIONS = %w[Added Changed Deprecated Removed Fixed Security].freeze
7
7
  KEY_PREFIX = "[kc]"
8
+ PROJECT_FILE_UPDATE_FIRST_LINE = /\A[-*]\s+\[kc\]\s+([^\s:]+):\s+updated\s+\d+\s+project\s+files?:\s*\z/
9
+ PROJECT_FILE_UPDATE_CATEGORY_LINE = /\A\s{2,}[-*]\s+(.+?)\s+\((\d+)\)\s*\z/
10
+
11
+ class << self
12
+ # Consolidates repeated maintenance summaries after a prepared release
13
+ # is updated in place. The AST supplies list-item boundaries; the two
14
+ # regular expressions only parse the stable text payload inside those
15
+ # already-classified Markdown items.
16
+ def collapse_project_file_updates(source)
17
+ require "ast/crispr/markdown/markly"
18
+ context = Ast::Crispr::Markdown::Markly.document_context(content: source.to_s, source_label: "CHANGELOG.md")
19
+ updates = context.structural_owners(owner_scope: :list_items)
20
+ .select { |item| item.depth == 1 }
21
+ .filter_map { |item| project_file_update_record(item) }
22
+ duplicate_groups = updates.group_by { |update| update.fetch(:key) }.values.select { |group| group.length > 1 }
23
+ return source.to_s if duplicate_groups.empty?
24
+
25
+ lines = source.to_s.lines
26
+ duplicate_groups.flatten.sort_by { |update| update.fetch(:start_line) }.reverse_each do |update|
27
+ group = duplicate_groups.find { |candidate| candidate.include?(update) }
28
+ first = group.min_by { |candidate| candidate.fetch(:start_line) }
29
+ replacement = (update == first) ? project_file_update_entry(group).lines : []
30
+ lines[(update.fetch(:start_line) - 1)...update.fetch(:end_line)] = replacement
31
+ end
32
+ lines.join
33
+ end
34
+
35
+ private
36
+
37
+ def project_file_update_record(item)
38
+ lines = item.source.to_s.lines
39
+ key_match = PROJECT_FILE_UPDATE_FIRST_LINE.match(lines.shift.to_s.strip)
40
+ return unless key_match
41
+
42
+ counts = lines.filter_map do |line|
43
+ match = PROJECT_FILE_UPDATE_CATEGORY_LINE.match(line)
44
+ next unless match
45
+
46
+ [match[1].tr(" ", "_").to_sym, match[2].to_i]
47
+ end.to_h
48
+ return if counts.empty?
49
+
50
+ {
51
+ key: key_match[1],
52
+ counts: counts,
53
+ start_line: item.location.start_line,
54
+ end_line: item.location.end_line
55
+ }
56
+ end
57
+
58
+ def project_file_update_entry(entries)
59
+ counts = entries.each_with_object(Hash.new(0)) do |entry, totals|
60
+ entry.fetch(:counts).each { |category, count| totals[category] += count }
61
+ end
62
+ total = counts.values.sum
63
+ details = counts.sort_by { |category, _count| category.to_s }.map do |category, count|
64
+ " - #{category.to_s.tr("_", " ")} (#{count})"
65
+ end
66
+ ["- #{KEY_PREFIX} #{entries.first.fetch(:key)}: updated #{total} project file#{"s" unless total == 1}:", *details, ""].join("\n")
67
+ end
68
+ end
8
69
 
9
70
  def initialize(section:, key:, entry:, legacy_prefixes: [], root: Kettle::Dev::CIHelpers.project_root)
10
71
  @root = root
@@ -5,7 +5,7 @@ module Kettle
5
5
  # Version namespace for this gem.
6
6
  module Version
7
7
  # Current gem version.
8
- VERSION = "1.0.5"
8
+ VERSION = "1.0.7"
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
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kettle-changelog
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter H. Boling
@@ -63,7 +63,7 @@ dependencies:
63
63
  requirements:
64
64
  - - ">="
65
65
  - !ruby/object:Gem::Version
66
- version: 3.0.15
66
+ version: 3.0.19
67
67
  - - "<"
68
68
  - !ruby/object:Gem::Version
69
69
  version: 4.0.0
@@ -73,7 +73,7 @@ dependencies:
73
73
  requirements:
74
74
  - - ">="
75
75
  - !ruby/object:Gem::Version
76
- version: 3.0.15
76
+ version: 3.0.19
77
77
  - - "<"
78
78
  - !ruby/object:Gem::Version
79
79
  version: 4.0.0
@@ -304,10 +304,10 @@ licenses:
304
304
  - AGPL-3.0-only
305
305
  metadata:
306
306
  homepage_uri: https://kettle-changelog.galtzo.com
307
- source_code_uri: https://github.com/kettle-dev/kettle-changelog/tree/v1.0.5
308
- changelog_uri: https://github.com/kettle-dev/kettle-changelog/blob/v1.0.5/CHANGELOG.md
307
+ source_code_uri: https://github.com/kettle-dev/kettle-changelog/tree/v1.0.7
308
+ changelog_uri: https://github.com/kettle-dev/kettle-changelog/blob/v1.0.7/CHANGELOG.md
309
309
  bug_tracker_uri: https://github.com/kettle-dev/kettle-changelog/issues
310
- documentation_uri: https://www.rubydoc.info/gems/kettle-changelog/1.0.5
310
+ documentation_uri: https://www.rubydoc.info/gems/kettle-changelog/1.0.7
311
311
  funding_uri: https://github.com/sponsors/pboling
312
312
  wiki_uri: https://github.com/kettle-dev/kettle-changelog/wiki
313
313
  news_uri: https://www.railsbling.com/tags/kettle-changelog
metadata.gz.sig CHANGED
Binary file