class Simp::ComponentInfo
Class that provides component version, release, and changelog information
Constants
- CHANGELOG_ENTRY_REGEX
See fedoraproject.org/wiki/Packaging:Guidelines?rd=Packaging/Guidelines#Changelogs When matched against this regex
match 1 = date of the form {weekday} {month} {day} {year} match 2 = author of the form {name} <{email}> match 3 = version match 4 = optional release qualifier; nil when absent
- ERR_MARKER
A helpful method for ensuring that the errors can be easily seen
Attributes
Public Class Methods
Load component information from appropriate component files
Module
version: Top-level 'version' key from the metadata.json file release: Not set changelog: Array of valid changelog entries derived from the CHANGELOG file
Asset
version: Primary Version tag from build/<component>.spec release: Primary Release tag from build/<component>.spec changelog: Array of valid changelog entries derived from the contents of the %changelog section in build/<component>.spec. Will be an empty if %changelog is not present. NOTES: 1. The changelog is only parsed up to the first entry that fails basic validation. - First line must be of the form * {date string} {author info} - {version} * {date string} {author info} - {version}-{release} where, date string = {weekday} {month} {day} {year} author info = {author name} <{author email}> - Weekday must be correct for the specified date - Entries must be separated by a blank line NOTE: This currently does not support the valid RPM `%changelog` format that places the version number on the next line: * Fri Mar 02 2012 Maintenance 4.0.0-2 - Improved test stubs. However, since we are not using this form for recent changelogs and we stop processing upon reaching such a changelog entry, this should *not* be an issue. 2. When RPM spec files contain sub-packages, only the primary package information is returned. 3. Some assets have another version in a lib/.../version.rb. Since there is no definitive way for this code to determine that version, it will not be loaded here.
Fails if any of the following occur:
-
The metadata.json file for a Puppet module component cannot be parsed.
-
A top-level ‘version’ key does not exist in the metadata.json file.
-
The CHANGELOG file for a Puppet module component does not exist.
-
The RPM spec file for a non-Puppet module component does not exist.
-
More than 1 RPM spec file for a non-Puppet module component exists.
-
The version, release or changelog cannot be extracted from the RPM spec file for a non-Puppet module.
-
Any changelog entry below the first entry has a version greater
than that of the first entry. Changelog entries must be ordered from latest version to earliest version.
-
The changelog entries are out of date order.
component_dir
-
The root directory of the component project.
latest_version_only
-
Whether to only return the changelog
entries for the latest version
verbose
-
Whether to log a changelog validation failure
# File lib/simp/componentinfo.rb, line 85 def initialize(component_dir, latest_version_only = false, verbose = true) @component_dir = component_dir if File.exist?(File.join(@component_dir, 'metadata.json')) @type = :module load_module_info(latest_version_only, verbose) else @type = :asset load_asset_info(latest_version_only, verbose) end end
Private Instance Methods
# File lib/simp/componentinfo.rb, line 116 def load_asset_info(latest_version_only, verbose) rpm_spec_files = Dir.glob(File.join(@component_dir, 'build', '*.spec')) if rpm_spec_files.empty? fail("No RPM spec file found in #{File.join(@component_dir, 'build')}") elsif rpm_spec_files.size > 1 fail("More than 1 RPM spec file found: #{rpm_spec_files.join(' ')}") end # Determine asset version, which we will ASSUME to be the main # package version. The RPM query, below, will return the main # package followed by subpackages. version_query = "rpm -q --queryformat '%{VERSION} %{RELEASE}\\n'" + " --specfile #{rpm_spec_files[0]}" rpm_version_list = `#{version_query} 2> /dev/null` if $?.exitstatus != 0 fail("Could not extract version and release from #{rpm_spec_files[0]}." + " To debug, execute:\n #{version_query}") end @version, @release = rpm_version_list.split("\n")[0].split changelog_query = "rpm -q --changelog --specfile #{rpm_spec_files[0]}" raw_changelog = `#{changelog_query} 2> /dev/null` if $?.exitstatus != 0 fail("Could not extract changelog from #{rpm_spec_files[0]}." + " To debug, execute:\n #{changelog_query}") elsif raw_changelog.strip.empty? changelog_lines = [] in_changelog = false File.read(rpm_spec_files[0]).lines.each do |line| changelog_lines << line if in_changelog if line.start_with?('%') if line.start_with?('%changelog') in_changelog = true else in_changelog = false end end end raw_changelog = changelog_lines.join end @changelog = parse_changelog(raw_changelog, latest_version_only, verbose) end
# File lib/simp/componentinfo.rb, line 98 def load_module_info(latest_version_only, verbose) require 'json' metadata_file = File.join(@component_dir, 'metadata.json') metadata = JSON.parse(File.read(metadata_file)) fail("ERROR: Version missing from #{metadata_file}") if metadata['version'].nil? @version = metadata['version'].split('-').first rel_bits = metadata['version'].split('-')[1..-1] @release = rel_bits.empty? ? nil : rel_bits.join('-') changelog_file = File.join(component_dir, 'CHANGELOG') unless File.exist?(changelog_file) fail("ERROR: No CHANGELOG file found in #{component_dir}") end @changelog = parse_changelog(IO.read(changelog_file), latest_version_only, verbose) end
Return an array of changelog entries, optionally for only the latest version
Iterates through the changelog entries from the newest to the oldest, performing basic validation. Stops processing entries if an entry fails validation.
# File lib/simp/componentinfo.rb, line 170 def parse_changelog(changelog, latest_version_only, verbose) # split on the entry-separating lines changelog_entries = changelog.split(/\n\n+/) latest_version = nil # 1st version found is latest version prev_entry_date = nil changelogs = [] changelog_entries.each do |entry| # split each entry into lines, removing the initial, empty line # that occurs on all but the first entry changelog_lines = entry.split("\n").delete_if { |line| line.empty? } match = CHANGELOG_ENTRY_REGEX.match(changelog_lines[0]) if match.nil? warn "WARNING: Parsing stopped at invalid changelog entry: \n#{entry}" if verbose break else # verify 1st version is latest version # NOTE: There are edge cases in which comparisons between # versions with and without release qualifiers may give answers # that are not expected. For example, '6.2.0' > '6.2.0-1'. full_version = match[3] full_version += "-#{match[4]}" unless match[4].nil? current_version = Gem::Version.new(full_version) latest_version = current_version if latest_version.nil? if current_version > latest_version fail("ERROR: Changelog entries are not properly version ordered") end break if latest_version_only and (current_version < latest_version) # verify dates are appropriately ordered (newest to oldest) current_entry_date = Date.strptime(match[1], '%a %b %d %Y') prev_entry_date = current_entry_date if prev_entry_date.nil? if current_entry_date > prev_entry_date fail("ERROR: Changelog entries are not properly date ordered") end if valid_date_weekday?(match[1], verbose) entry = { :date => match[1], :version => match[3], :release => match[4], :content => changelog_lines } changelogs << entry else warn "WARNING: Parsing stopped at invalid changelog entry: \n#{entry}" if verbose break end end end changelogs end
Validate the weekday in the already-format-verified changelog date string is correct for the date specified
Returns false if the weekday is incorrect for date specified.
changelog_date
-
Date string of the form <weekday> <month> <day> <year>
verbose
-
Whether to log details about a weekday validation failure
# File lib/simp/componentinfo.rb, line 231 def valid_date_weekday?(changelog_date, verbose) date = Date.strptime(changelog_date, '%a %b %d %Y') expected_weekday = date.strftime('%a') actual_weekday = changelog_date.strip.split[0] valid = true if actual_weekday != expected_weekday err_msg = ERR_MARKER + "'#{actual_weekday}' should be '#{expected_weekday}' for" + " changelog timestamp '#{changelog_date}'" warn err_msg if verbose valid = false end return valid end