Skip to content

Commit 1239211

Browse files
committed
Add rake task for component diff between versions
This adds a new rake task. It allows us to output the component updates between two provided versions. This is useful for consuming project like openbolt and openvox. They usually skip a few puppet-runtime releases. The new task allows those projects to diff their previous to new puppet-runtime version and get a component overview. example for the openbolt 5.5.0->5.6.0 release: ``` bundle exec rake release:changelog_components:diff[2026.04.20.1,2026.06.10.1,false] **Component Changes:** | Component | Old Version | New Version | |-----------|-------------|-------------| | curl | 8.19.0 | 8.20.0 | | libxml2 | 2.15.2 | 2.15.3 | | openssl-3.0 | 3.0.20 | 3.0.21 | | openssl-3.5 | | 3.5.7 | | ruby-4.0 | | 4.0.5 | | ruby-selinux | 3.9 | 3.10 | | ruby-shadow-patched | | bc7752a9ddbde06c1418734d003a9607bafcc6df | | rubygem-aws-partitions | 1.1240.0 | 1.1259.0 | | rubygem-aws-sdk-core | 3.245.0 | 3.251.0 | | rubygem-aws-sdk-ec2 | 1.612.0 | 1.622.0 | | rubygem-excon | 1.4.2 | 1.5.0 | | rubygem-faraday | 2.14.1 | 2.14.2 | | rubygem-faraday-net_http | 3.4.2 | 3.4.4 | | rubygem-openfact | 5.6.0 | 5.6.1 | | rubygem-openvox | 8.26.2 | 8.28.0 | | rubygem-yard | 0.9.43 | 0.9.44 | ``` And latest puppet-runtime releases: ``` $ bundle exec rake release:changelog_components:diff[2026.06.09.1,2026.06.10.1,false] **Component Changes:** | Component | Old Version | New Version | |-----------|-------------|-------------| | rubygem-aws-partitions | 1.1258.0 | 1.1259.0 | | rubygem-aws-sdk-ec2 | 1.621.0 | 1.622.0 | | rubygem-openvox | 8.27.0 | 8.28.0 | ``` We can also get a list of added components in various projects: ``` **Component Changes:** | Component | Old Version | New Version | |-----------|-------------|-------------| | curl | 8.19.0 | 8.20.0 | | libxml2 | 2.15.2 | 2.15.3 | | openssl-3.0 | 3.0.20 | 3.0.21 | | openssl-3.5 | | 3.5.7 | | ruby-4.0 | | 4.0.5 | | ruby-selinux | 3.9 | 3.10 | | ruby-shadow-patched | | bc7752a9ddbde06c1418734d003a9607bafcc6df | | rubygem-aws-partitions | 1.1240.0 | 1.1259.0 | | rubygem-aws-sdk-core | 3.245.0 | 3.251.0 | | rubygem-aws-sdk-ec2 | 1.612.0 | 1.622.0 | | rubygem-excon | 1.4.2 | 1.5.0 | | rubygem-faraday | 2.14.1 | 2.14.2 | | rubygem-faraday-net_http | 3.4.2 | 3.4.4 | | rubygem-openfact | 5.6.0 | 5.6.1 | | rubygem-openvox | 8.26.2 | 8.28.0 | | rubygem-yard | 0.9.43 | 0.9.44 | **Project component additions:** - openssl-3.5: agent-runtime-main - ruby-4.0: agent-runtime-main - ruby-shadow-patched: agent-runtime-main ``` This is required for the puppet-runtime changelog itself, but not so much in the individual projects, so it's configureable. Signed-off-by: Tim Meusel <tim@bastelfreak.de>
1 parent d3feeca commit 1239211

1 file changed

Lines changed: 71 additions & 29 deletions

File tree

tasks/vox.rake

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,33 @@ class Version
5252
end
5353
end
5454

55+
def build_component_change_lines(prev_data, new_data, include_project_component_additions: true)
56+
header_lines = ["\n**Component Changes:**\n", "| Component | Old Version | New Version |\n", "|-----------|-------------|-------------|\n"]
57+
component_lines = []
58+
new_components = []
59+
60+
new_data['components'].sort.each do |comp, ver|
61+
prev_ver = prev_data['components'][comp]
62+
next if prev_ver == ver
63+
64+
new_components << comp if prev_ver.nil?
65+
component_lines << "| #{comp} | #{prev_ver} | #{ver} |\n"
66+
end
67+
component_lines = header_lines + component_lines unless component_lines.empty?
68+
69+
if include_project_component_additions && !new_components.empty?
70+
component_lines << "\n**Project component additions:**\n"
71+
new_components.each do |component|
72+
projects = new_data['projects']
73+
.select { |_, platforms| platforms.values.any? { |components| components.key?(component) } }
74+
.keys
75+
component_lines << "- #{component}: #{projects.join(', ')}\n"
76+
end
77+
end
78+
79+
component_lines
80+
end
81+
5582
desc 'Set the full version of the project'
5683
task 'vox:version:bump:full' do
5784
puts 'This project use the current date as version number. No bump needed.'
@@ -107,49 +134,64 @@ else
107134
end
108135
# rubocop:enable Rake/DuplicateTask
109136

110-
desc 'Inject component change information into changelog'
111-
task 'release:changelog_components', ['tag'] do |_, args|
112-
abort 'You must provide the tag that will be used for this release.' if args[:tag].nil? || args[:tag].empty?
137+
desc 'Show component change information between two tags from component_info.json'
138+
task 'release:changelog_components:diff', %i[old_tag new_tag include_project_component_additions] do |_, args|
139+
abort 'You must provide old_tag and new_tag, e.g. rake "release:changelog_components:diff[2026.06.11.1,2026.06.12.1,true]"' if args[:old_tag].to_s.empty? || args[:new_tag].to_s.empty?
113140

114-
changelog = File.expand_path('../CHANGELOG.md', __dir__)
115141
data_file = File.expand_path('../component_info.json', __dir__)
116142
data = JSON.parse(File.read(data_file))
117143

118-
abort "No component information found for tag #{args[:tag]} in #{data_file}" unless data.key?(args[:tag])
119-
abort "Data for tag #{args[:tag]} does not appear at the top of the file." unless data.keys.first == args[:tag]
144+
abort "No component information found for old_tag #{args[:old_tag]} in #{data_file}" unless data.key?(args[:old_tag])
145+
abort "No component information found for new_tag #{args[:new_tag]} in #{data_file}" unless data.key?(args[:new_tag])
120146

121-
prev_data = data.values[1]
122-
new_data = data.values[0]
147+
include_project_component_additions = !%w[0 false no off].include?(args[:include_project_component_additions].to_s.downcase)
123148

124-
header_lines = ["\n**Component Changes:**\n", "| Component | Old Version | New Version |\n", "|-----------|-------------|-------------|\n"]
125-
component_lines = []
126-
new_components = []
127-
data[args[:tag]]['components'].sort.each do |comp, ver|
128-
prev_ver = prev_data['components'][comp]
129-
next if prev_ver == ver
149+
component_lines = build_component_change_lines(
150+
data[args[:old_tag]],
151+
data[args[:new_tag]],
152+
include_project_component_additions: include_project_component_additions
153+
)
130154

131-
new_components << comp if prev_ver.nil?
132-
component_lines << "| #{comp} | #{prev_data['components'][comp]} | #{ver} |\n"
155+
if component_lines.empty?
156+
puts "No component changes detected between #{args[:old_tag]} and #{args[:new_tag]}"
157+
else
158+
puts component_lines.join
133159
end
134-
component_lines = header_lines + component_lines unless component_lines.empty?
160+
end
135161

136-
unless new_components.empty?
137-
component_lines << "\n**Project component additions:**\n"
138-
new_components.each do |component|
139-
projects = new_data['projects']
140-
.select { |_, platforms| platforms.values.any? { |components| components.key?(component) } }
141-
.keys
142-
component_lines << "- #{component}: #{projects.join(', ')}\n"
143-
end
144-
end
162+
desc 'Inject component change information into changelog'
163+
task 'release:changelog_components', ['tag'] do |_, args|
164+
changelog = File.expand_path('../CHANGELOG.md', __dir__)
165+
data_file = File.expand_path('../component_info.json', __dir__)
166+
data = JSON.parse(File.read(data_file))
167+
168+
tag = args[:tag].to_s.empty? ? data.keys.first : args[:tag]
169+
abort "No component information found for tag #{tag} in #{data_file}" unless data.key?(tag)
170+
tag_index = data.keys.index(tag)
171+
prev_tag = data.keys[tag_index + 1]
172+
abort "No previous tag data found in #{data_file} to diff against #{tag}" if prev_tag.nil?
173+
174+
prev_data = data[prev_tag]
175+
new_data = data[tag]
176+
component_lines = build_component_change_lines(prev_data, new_data)
145177

146178
if component_lines.empty?
147-
puts "No component changes detected for tag #{args[:tag]}"
179+
puts "No component changes detected for tag #{tag}"
148180
else
149181
content = File.read(changelog)
150-
new_content = content.sub('**Merged pull requests:**', "#{component_lines.join}\n**Merged pull requests:**")
182+
183+
section_regex = /^## \[#{Regexp.escape(tag)}\].*?(?=^## \[|\z)/m
184+
section_match = content.match(section_regex)
185+
abort "Could not find release section for tag #{tag} in #{changelog}" if section_match.nil?
186+
187+
section = section_match[0]
188+
abort "Release section for tag #{tag} does not contain a Full Changelog line" unless section.match?(/^\[Full Changelog\]\(/)
189+
190+
new_section = section.sub(/^\[Full Changelog\]\([^\n]+\)\n/, "\\0\n#{component_lines.join}\n")
191+
new_content = content.sub(section, new_section)
192+
151193
File.write(changelog, new_content)
152-
puts "Injected component change information into #{changelog} for tag #{args[:tag]}"
194+
puts "Injected component change information into #{changelog} for tag #{tag}"
153195
end
154196
end
155197

0 commit comments

Comments
 (0)