simp-rake-helpers 5.1.4 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/lib/simp/componentinfo.rb +227 -0
  4. data/lib/simp/rake/helpers/version.rb +1 -1
  5. data/lib/simp/rake/pkg.rb +115 -0
  6. data/lib/simp/rake/pupmod/helpers.rb +2 -2
  7. data/lib/simp/relchecks.rb +172 -0
  8. data/lib/simp/rpm.rb +2 -0
  9. data/spec/acceptance/nodesets/default.yml +2 -0
  10. data/spec/acceptance/pkg_rpm_spec.rb +42 -133
  11. data/spec/lib/simp/componentinfo_changelog_regex_spec.rb +120 -0
  12. data/spec/lib/simp/componentinfo_spec.rb +278 -0
  13. data/spec/lib/simp/files/componentinfo_spec/asset_missing_release/build/asset_missing_release.spec +36 -0
  14. data/spec/lib/simp/files/componentinfo_spec/asset_missing_version/build/asset_missing_version.spec +36 -0
  15. data/spec/lib/simp/files/componentinfo_spec/asset_with_dist_in_release/build/asset_with_dist_in_release.spec +31 -0
  16. data/spec/lib/simp/files/componentinfo_spec/asset_with_multiple_packages/build/asset_with_multiple_packages.spec +64 -0
  17. data/spec/lib/simp/files/componentinfo_spec/asset_with_single_package/build/asset_with_single_package.spec +37 -0
  18. data/spec/lib/simp/files/componentinfo_spec/asset_with_two_spec_files/build/asseta.spec +43 -0
  19. data/spec/lib/simp/files/componentinfo_spec/asset_with_two_spec_files/build/assetb.spec +43 -0
  20. data/spec/lib/simp/files/componentinfo_spec/asset_without_spec_file/build/README +1 -0
  21. data/spec/lib/simp/files/componentinfo_spec/module/CHANGELOG +14 -0
  22. data/spec/lib/simp/files/componentinfo_spec/module/metadata.json +44 -0
  23. data/spec/lib/simp/files/componentinfo_spec/module_missing_version_metadata/CHANGELOG +14 -0
  24. data/spec/lib/simp/files/componentinfo_spec/module_missing_version_metadata/metadata.json +43 -0
  25. data/spec/lib/simp/files/componentinfo_spec/module_with_date_misordered_entries/CHANGELOG +14 -0
  26. data/spec/lib/simp/files/componentinfo_spec/module_with_date_misordered_entries/metadata.json +44 -0
  27. data/spec/lib/simp/files/componentinfo_spec/module_with_invalid_entries/CHANGELOG +8 -0
  28. data/spec/lib/simp/files/componentinfo_spec/module_with_invalid_entries/metadata.json +44 -0
  29. data/spec/lib/simp/files/componentinfo_spec/module_with_invalid_weekday_entry/CHANGELOG +14 -0
  30. data/spec/lib/simp/files/componentinfo_spec/module_with_invalid_weekday_entry/metadata.json +44 -0
  31. data/spec/lib/simp/files/componentinfo_spec/module_with_malformed_metadata/CHANGELOG +14 -0
  32. data/spec/lib/simp/files/componentinfo_spec/module_with_malformed_metadata/metadata.json +44 -0
  33. data/spec/lib/simp/files/componentinfo_spec/module_with_version_misordered_entries/CHANGELOG +14 -0
  34. data/spec/lib/simp/files/componentinfo_spec/module_with_version_misordered_entries/metadata.json +44 -0
  35. data/spec/lib/simp/files/componentinfo_spec/module_without_changelog/metadata.json +44 -0
  36. data/spec/lib/simp/files/relchecks_compare_latest_tag_spec/module/CHANGELOG +5 -0
  37. data/spec/lib/simp/files/relchecks_compare_latest_tag_spec/module/metadata.json +44 -0
  38. data/spec/lib/simp/files/relchecks_compare_latest_tag_spec/module_without_changelog/metadata.json +44 -0
  39. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/asset_mismatched_release/build/asset_mismatched_release.spec +35 -0
  40. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/asset_missing_changelog/build/asset_missing_changelog.spec +27 -0
  41. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/asset_with_dist_in_release/build/asset_with_dist_in_release.spec +31 -0
  42. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/asset_with_multiple_packages/build/asset_with_multiple_packages.spec +64 -0
  43. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/asset_with_single_package/build/asset_with_single_package.spec +35 -0
  44. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/asset_without_spec_file/build/README +1 -0
  45. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_misordered_entries/CHANGELOG +14 -0
  46. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_misordered_entries/metadata.json +44 -0
  47. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_multiple_entries/CHANGELOG +14 -0
  48. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_multiple_entries/metadata.json +44 -0
  49. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_newer_changelog_entry/CHANGELOG +14 -0
  50. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_newer_changelog_entry/metadata.json +44 -0
  51. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_no_entry_for_version/CHANGELOG +14 -0
  52. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_no_entry_for_version/metadata.json +44 -0
  53. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_single_entry/CHANGELOG +7 -0
  54. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_with_single_entry/metadata.json +44 -0
  55. data/spec/lib/simp/files/relchecks_create_tag_changelog_spec/module_without_changelog/metadata.json +44 -0
  56. data/spec/lib/simp/relchecks_compare_latest_tag_spec.rb +89 -0
  57. data/spec/lib/simp/relchecks_create_tag_changelog_spec.rb +143 -0
  58. metadata +51 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3172c665fe5f42370b01ef6a03b7530da556471e89a7b55a90e23b8cc5c899e5
4
- data.tar.gz: 377fad7a7d2acccd401c1cf17e52548144a27bbcd220618e0caca66e7e3cadea
3
+ metadata.gz: 476fb45c0548c83de14eec268b1970b990a0c50ef8ead160f502bb3c34fa3bd6
4
+ data.tar.gz: 0d6dde21abf6befaf136b973f324d2cd812f0d36d49c0a6e48971499b0487802
5
5
  SHA512:
6
- metadata.gz: fa1a4fc7b92292ceb0908368d43768ca70ec77ff31efd8e9d5c4def2dec3b90c213066f9843eaf258b070e4cfe53aeae8f0066cad24a062b41992e3da005f3f2
7
- data.tar.gz: 3dd45d6438797ba3a8173ee33b511f90319725b5602930f3e75068bdc4002d485e58b4a58dbcdc684dc46f7c6af1dcd53766c39f44aae95b9c036a8cee976d74
6
+ metadata.gz: f090fd69b70d38e94f0a71ba57d3e76782b10a7ef6e7987c8a4b8665b17a29b2b5c9b7065c0b150979544f09203b8ac385fea194b2bf5672b44499868302a9e0
7
+ data.tar.gz: 3671a8e5759091753650ca03fcddfbc68ada6c85fcb12e6c33e6d803e3b9c3d78eafbcf858ef828632ba085d9fe831abd3d4d124227e83afaf7a408c8f310a6c
@@ -1,3 +1,21 @@
1
+ ### 5.2.0 / 2017-12-20
2
+ * Create pkg:create_tag_changelog, which is a more thorough version
3
+ of Simp::Rake::Pupmod::Helpers changelog_annotation task.
4
+ - Now supports non-Puppet SIMP assets for which version and changelog
5
+ information is specified in an RPM spec files.
6
+ - Provides more extensive validation of date strings and changelog
7
+ entry ordering.
8
+ - Stops processing at the first invalid changelog entry, to minimize
9
+ non-catestrophic errors from old changelog entries.
10
+ * Create pkg:compare_latest_tag, which is a more general replacement
11
+ for the Simp::Rake::Pupmod::Helpers compare_latest_tag task.
12
+ - Now supports non-Puppet SIMP assets for which version and changelog
13
+ information is specified in an RPM spec files.
14
+ - Does the same validation as the new pkg:create_tag_changelog task.
15
+ * Fix broken acceptance tests
16
+ - Remove logic to build SIMP 4 and SIMP 5 RPMs.
17
+ - Remove mock logic
18
+
1
19
  ### 5.1.4 / 2017-11-27
2
20
  * Switch back to using Gem::Version.new instead of Puppet's vercmp since
3
21
  Gem::Version matches the standard RPM version semantics and Puppet does not.
@@ -0,0 +1,227 @@
1
+ require 'date'
2
+
3
+ module Simp; end
4
+
5
+ # Class that provides component version, release, and changelog
6
+ # information
7
+ class Simp::ComponentInfo
8
+ attr_accessor :component_dir, :type, :version, :release, :changelog
9
+
10
+ # A helpful method for ensuring that the errors can be easily seen
11
+ ERR_MARKER = "WARNING: !!! "
12
+
13
+ # See https://fedoraproject.org/wiki/Packaging:Guidelines?rd=Packaging/Guidelines#Changelogs
14
+ # When matched against this regex
15
+ # match 1 = date of the form {weekday} {month} {day} {year}
16
+ # match 2 = author of the form {name} <{email}>
17
+ # match 3 = version
18
+ # match 4 = optional release qualifier; nil when absent
19
+ CHANGELOG_ENTRY_REGEX = /^\*\s+((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [0-3]{1}[0-9]{1} \d{4})\s+(.+<.+>)(?:\s+|\s*-\s*)(\d+\.\d+\.\d+)(?:-(\S+))?\s*$/
20
+
21
+ # Load component information from appropriate component files
22
+ #
23
+ # Module
24
+ # version: Top-level 'version' key from the metadata.json file
25
+ # release: Not set
26
+ # changelog: Array of valid changelog entries derived from the
27
+ # CHANGELOG file
28
+ #
29
+ # Asset
30
+ # version: Primary Version tag from build/<component>.spec
31
+ # release: Primary Release tag from build/<component>.spec
32
+ # changelog: Array of valid changelog entries derived from the
33
+ # contents of the %changelog section in
34
+ # build/<component>.spec. Will be an empty if
35
+ # %changelog is not present.
36
+ #
37
+ # NOTES:
38
+ # 1. The changelog is only parsed up to the first entry that
39
+ # fails basic validation.
40
+ # - First line must be of the form
41
+ # * {date string} {author info} - {version}
42
+ # * {date string} {author info} - {version}-{release}
43
+ #
44
+ # where,
45
+ # date string = {weekday} {month} {day} {year}
46
+ # author info = {author name} <{author email}>
47
+ # - Weekday must be correct for the specified date
48
+ # - Entries must be separated by a blank line
49
+ #
50
+ # NOTE: This currently does not support the valid RPM `%changelog`
51
+ # format that places the version number on the next line:
52
+ #
53
+ # * Fri Mar 02 2012 Maintenance
54
+ # 4.0.0-2
55
+ # - Improved test stubs.
56
+ #
57
+ # However, since we are not using this form for recent
58
+ # changelogs and we stop processing upon reaching such
59
+ # a changelog entry, this should *not* be an issue.
60
+ # 2. When RPM spec files contain sub-packages, only the primary
61
+ # package information is returned.
62
+ # 3. Some assets have another version in a lib/.../version.rb.
63
+ # Since there is no definitive way for this code to determine
64
+ # that version, it will not be loaded here.
65
+ #
66
+ # Fails if any of the following occur:
67
+ # - The metadata.json file for a Puppet module component cannot be
68
+ # parsed.
69
+ # - A top-level 'version' key does not exist in the metadata.json file.
70
+ # - The CHANGELOG file for a Puppet module component does not exist.
71
+ # - The RPM spec file for a non-Puppet module component does not exist.
72
+ # - More than 1 RPM spec file for a non-Puppet module component exists.
73
+ # - The version, release or changelog cannot be extracted from the RPM
74
+ # spec file for a non-Puppet module.
75
+ # - Any changelog entry below the first entry has a version greater
76
+ # than that of the first entry. Changelog entries must be
77
+ # ordered from latest version to earliest version.
78
+ # - The changelog entries are out of date order.
79
+ #
80
+ # +component_dir+:: The root directory of the component project.
81
+ # +latest_version_only+:: Whether to only return the changelog
82
+ # entries for the latest version
83
+ # +verbose+:: Whether to log a changelog validation failure
84
+ #
85
+ def initialize(component_dir, latest_version_only = false, verbose = true)
86
+ @component_dir = component_dir
87
+
88
+ if File.exists?(File.join(@component_dir, 'metadata.json'))
89
+ @type = :module
90
+ load_module_info(latest_version_only, verbose)
91
+ else
92
+ @type = :asset
93
+ load_asset_info(latest_version_only, verbose)
94
+ end
95
+ end
96
+
97
+ private
98
+ def load_module_info(latest_version_only, verbose)
99
+ require 'json'
100
+ metadata_file = File.join(@component_dir, 'metadata.json')
101
+ metadata = JSON.parse(File.read(metadata_file))
102
+ @version = metadata['version']
103
+ @release = nil
104
+ fail("ERROR: Version missing from #{metadata_file}") if @version.nil?
105
+
106
+ changelog_file = File.join(component_dir, 'CHANGELOG')
107
+ unless File.exists?(changelog_file)
108
+ fail("ERROR: No CHANGELOG file found in #{component_dir}")
109
+ end
110
+ @changelog = parse_changelog(IO.read(changelog_file), latest_version_only, verbose)
111
+ end
112
+
113
+ def load_asset_info(latest_version_only, verbose)
114
+ rpm_spec_files = Dir.glob(File.join(@component_dir, 'build', '*.spec'))
115
+ if rpm_spec_files.empty?
116
+ fail("No RPM spec file found in #{File.join(@component_dir, 'build')}")
117
+ elsif rpm_spec_files.size > 1
118
+ fail("More than 1 RPM spec file found: #{rpm_spec_files.join(' ')}")
119
+ end
120
+
121
+ # Determine asset version, which we will ASSUME to be the main
122
+ # package version. The RPM query, below, will return the main
123
+ # package followed by subpackages.
124
+ version_query = "rpm -q --queryformat '%{VERSION} %{RELEASE}\\n'" +
125
+ " --specfile #{rpm_spec_files[0]}"
126
+
127
+ rpm_version_list = `#{version_query} 2> /dev/null`
128
+ if $?.exitstatus != 0
129
+ fail("Could not extract version and release from #{rpm_spec_files[0]}." +
130
+ " To debug, execute:\n #{version_query}")
131
+ end
132
+ @version, @release = rpm_version_list.split("\n")[0].split
133
+
134
+ changelog_query = "rpm -q --changelog --specfile #{rpm_spec_files[0]}"
135
+ raw_changelog = `#{changelog_query} 2> /dev/null`
136
+ if $?.exitstatus != 0
137
+ fail("Could not extract changelog from #{rpm_spec_files[0]}." +
138
+ " To debug, execute:\n #{changelog_query}")
139
+ end
140
+ @changelog = parse_changelog(raw_changelog, latest_version_only, verbose)
141
+ end
142
+
143
+ # Return an array of changelog entries, optionally for only the
144
+ # latest version
145
+ #
146
+ # Iterates through the changelog entries from the newest to the
147
+ # oldest, performing basic validation. Stops processing entries
148
+ # if an entry fails validation.
149
+ #
150
+ def parse_changelog(changelog, latest_version_only, verbose)
151
+ # split on the entry-separating lines
152
+ changelog_entries = changelog.split(/^\s*$/)
153
+ latest_version = nil # 1st version found is latest version
154
+ prev_entry_date = nil
155
+ changelogs = []
156
+ changelog_entries.each do |entry|
157
+ # split each entry into lines, removing the initial, empty line
158
+ # that occurs on all but the first entry
159
+ changelog_lines = entry.split("\n").delete_if { |line| line.empty? }
160
+ match = CHANGELOG_ENTRY_REGEX.match(changelog_lines[0])
161
+ if match.nil?
162
+ warn "WARNING: Parsing stopped at invalid changelog entry: \n#{entry}" if verbose
163
+ break
164
+ else
165
+ # verify 1st version is latest version
166
+ # NOTE: There are edge cases in which comparisons between
167
+ # versions with and without release qualifiers may give answers
168
+ # that are not expected. For example, '6.2.0' > '6.2.0-1'.
169
+ full_version = match[3]
170
+ full_version += "-#{match[4]}" unless match[4].nil?
171
+ current_version = Gem::Version.new(full_version)
172
+ latest_version = current_version if latest_version.nil?
173
+
174
+ if current_version > latest_version
175
+ fail("ERROR: Changelog entries are not properly version ordered")
176
+ end
177
+
178
+ break if latest_version_only and (current_version < latest_version)
179
+
180
+ # verify dates are appropriately ordered (newest to oldest)
181
+ current_entry_date = Date.strptime(match[1], '%a %b %d %Y')
182
+ prev_entry_date = current_entry_date if prev_entry_date.nil?
183
+ if current_entry_date > prev_entry_date
184
+ fail("ERROR: Changelog entries are not properly date ordered")
185
+ end
186
+
187
+ if valid_date_weekday?(match[1], verbose)
188
+ entry = {
189
+ :date => match[1],
190
+ :version => match[3],
191
+ :release => match[4],
192
+ :content => changelog_lines
193
+ }
194
+ changelogs << entry
195
+ else
196
+ warn "WARNING: Parsing stopped at invalid changelog entry: \n#{entry}" if verbose
197
+ break
198
+ end
199
+ end
200
+ end
201
+
202
+ changelogs
203
+ end
204
+
205
+ # Validate the weekday in the already-format-verified changelog
206
+ # date string is correct for the date specified
207
+ #
208
+ # Returns false if the weekday is incorrect for date specified.
209
+ #
210
+ # +changelog_date+:: Date string of the form <weekday> <month> <day> <year>
211
+ # +verbose+:: Whether to log details about a weekday validation failure
212
+ def valid_date_weekday?(changelog_date, verbose)
213
+ date = Date.strptime(changelog_date, '%a %b %d %Y')
214
+ expected_weekday = date.strftime('%a')
215
+ actual_weekday = changelog_date.strip.split[0]
216
+
217
+ valid = true
218
+ if actual_weekday != expected_weekday
219
+ err_msg = ERR_MARKER + "'#{actual_weekday}' should be '#{expected_weekday}' for" +
220
+ " changelog timestamp '#{changelog_date}'"
221
+ warn err_msg if verbose
222
+ valid = false
223
+ end
224
+ return valid
225
+ end
226
+
227
+ end
@@ -2,5 +2,5 @@ module Simp; end
2
2
  module Simp::Rake; end
3
3
 
4
4
  class Simp::Rake::Helpers
5
- VERSION = '5.1.4'
5
+ VERSION = '5.2.0'
6
6
  end
@@ -6,6 +6,7 @@ require 'rake/clean'
6
6
  require 'rake/tasklib'
7
7
  require 'fileutils'
8
8
  require 'find'
9
+ require 'simp/relchecks'
9
10
  require 'simp/rpm'
10
11
  require 'simp/rake/helpers/rpm_spec'
11
12
 
@@ -106,6 +107,8 @@ module Simp::Rake
106
107
  define_pkg_tar
107
108
  define_pkg_rpm
108
109
  define_pkg_check_version
110
+ define_pkg_compare_latest_tag
111
+ define_pkg_create_tag_changelog
109
112
  task :default => 'pkg:tar'
110
113
 
111
114
  Rake::Task['pkg:tar']
@@ -463,6 +466,118 @@ module Simp::Rake
463
466
  end
464
467
  end
465
468
 
469
+ def define_pkg_compare_latest_tag
470
+ namespace :pkg do
471
+ desc <<-EOM
472
+ Compare to latest tag.
473
+ ARGS:
474
+ * :tags_source => Set to the remote from which the tags for this
475
+ project can be fetched. Defaults to 'origin'.
476
+ * :verbose => Set to 'true' if you want to see detailed messages
477
+
478
+ NOTES:
479
+ Compares mission-impacting (significant) files with the latest
480
+ tag and identifies the relevant files that have changed.
481
+
482
+ Fails if
483
+ (1) There is any version validation or changelog parsing failure
484
+ that would prevent an annotated changelog tag from being
485
+ created. (See pkg::create_tag_changelog)
486
+ (2) A version bump is required but not recorded in both the
487
+ CHANGELOG and metadata.json files.
488
+ (3) The latest version is < latest tag.
489
+
490
+ Changes to the following files/directories are not considered
491
+ significant:
492
+ - Any hidden file/directory (entry that begins with a '.')
493
+ - Gemfile
494
+ - Gemfile.lock
495
+ - Rakefile
496
+ - spec directory
497
+ - doc directory
498
+ EOM
499
+ task :compare_latest_tag, [:tags_source, :verbose] do |t,args|
500
+ tags_source = args[:tags_source].nil? ? 'origin' : args[:tags_source]
501
+ if args[:verbose].to_s == 'true'
502
+ verbose = true
503
+ else
504
+ verbose = false
505
+ end
506
+ Simp::RelChecks::compare_latest_tag(@base_dir, tags_source, verbose)
507
+ end
508
+ end
509
+ end
510
+
511
+ def define_pkg_create_tag_changelog
512
+ namespace :pkg do
513
+ # :pkg:create_tag_changelog
514
+ # -----------------------------
515
+ desc <<-EOM
516
+ Generate an appropriate changelog for an annotated tag from a
517
+ component's CHANGELOG or RPM spec file.
518
+
519
+ The changelog text will be for the latest version and contain
520
+ 1 or more changelog entries for that version, in reverse
521
+ chronological order.
522
+
523
+ ARGS:
524
+ * verbose => Set to 'true' if you want to see
525
+ non-catestrophic warning messages.
526
+
527
+ NOTES:
528
+ * Changelog entries must follow the following rules:
529
+ - An entry must start with * and be terminated
530
+ by a blank line.
531
+ - The first line must be of the form
532
+ * Wed Jul 05 2017 Author Name <author@simp.com> - 1.2.3-4
533
+ - The date string must be RPM compatible.
534
+ - Dates must be in reverse chronological order, with the
535
+ newest dates occurring at the top of the changelog.
536
+ - Both an author name and email are required.
537
+ - The author email must be contained in < >.
538
+ - The version is required and must be of the form
539
+ <major>.<minor>.<patch>.
540
+ - The version may contain a release qualifier.
541
+ - When the release qualifier is present, it must appear
542
+ at the end of the version string and be separated from
543
+ the version by a '-'.
544
+
545
+ * This task will fail if any of the following occur:
546
+ - The metadata.json file for a Puppet module component
547
+ cannot be parsed.
548
+ - The CHANGELOG file for a Puppet module component does
549
+ not exist.
550
+ - The CHANGELOG entries for the latest version are
551
+ malformed.
552
+ - The RPM spec file or a non-Puppet module component does
553
+ not exist.
554
+ - More than 1 RPM spec file for a non-Puppet module
555
+ component exists.
556
+ - No valid changelog entries for the version specified in
557
+ the metadata.json/spec file are found.
558
+ - The latest changelog version is greater than the version
559
+ in the metadata.json or the RPM spec file.
560
+ - The RPM release specified in the spec file does not match
561
+ the release in a changelog entry for the version.
562
+ - Any changelog entry below the first entry has a version
563
+ greater than that of the first entry.
564
+ - The changelog entries for the latest version are out of
565
+ date order.
566
+ - The weekday for a changelog entry for the latest version
567
+ does not match the date specified.
568
+
569
+ EOM
570
+ task :create_tag_changelog, [:verbose] do |t,args|
571
+ if args[:verbose].to_s == 'true'
572
+ verbose = true
573
+ else
574
+ verbose = false
575
+ end
576
+ puts Simp::RelChecks::create_tag_changelog(@base_dir, verbose)
577
+ end
578
+ end
579
+ end
580
+
466
581
  # ------------------------------------------------------------------------------
467
582
  # helper methods
468
583
  # ------------------------------------------------------------------------------
@@ -251,9 +251,9 @@ class Simp::Rake::Pupmod::Helpers < ::Rake::TaskLib
251
251
  if curr_module_version < last_tag_version
252
252
  fail("ERROR: Version regression. '#{module_version}' < last tag '#{last_tag}'")
253
253
  elsif curr_module_version == last_tag_version
254
- fail("ERROR: Version update beyond last tag '#{last_tag}' is required for changes to #{files_changed}")
254
+ fail("ERROR: Version update beyond last tag '#{last_tag}' is required for #{files_changed.count} changed files:\n * #{files_changed.join("\n * ")}")
255
255
  else
256
- puts " New tag of version '#{module_version}' is required for changes to #{files_changed}"
256
+ puts "NOTICE: New tag of version '#{module_version}' is required for #{files_changed.count} changed files:\n * #{files_changed.join("\n * ")}"
257
257
  end
258
258
  end
259
259
  end
@@ -0,0 +1,172 @@
1
+ require 'date'
2
+ require 'simp/componentinfo'
3
+
4
+ module Simp; end
5
+
6
+ # Class that provide release-related checks
7
+ class Simp::RelChecks
8
+
9
+ # Compares mission-impacting (significant) files with the latest
10
+ # tag and identifies the relevant files that have changed.
11
+ #
12
+ # Fails if
13
+ # (1) There is any version validation or changelog parsing failure
14
+ # that would prevent an annotated changelog tag from being
15
+ # created. (See Simp::RelCheck::create_tag_changelog)
16
+ # (2) A version bump is required but not recorded in both the
17
+ # CHANGELOG and metadata.json files.
18
+ # (3) The latest version is < latest tag.
19
+
20
+ # Changes to the following files/directories are not considered
21
+ # significant:
22
+ # - Any hidden file/directory (entry that begins with a '.')
23
+ # - Gemfile
24
+ # - Gemfile.lock
25
+ # - Rakefile
26
+ # - spec directory
27
+ # - doc directory
28
+ #
29
+ # +component_dir+:: The root directory of the component project.
30
+ # +tags_source+:: The remote from which the tags for this project
31
+ # can be fetched.
32
+ # +verbose+:: Set to 'true' if you want to see detailed messages
33
+ def self.compare_latest_tag(component_dir, tags_source = 'origin', verbose = false)
34
+ info, changelogs = load_and_validate_changelog(component_dir, verbose)
35
+ Dir.chdir(component_dir) do
36
+ # determine last tag
37
+ `git fetch -t #{tags_source} 2>/dev/null`
38
+ tags = `git tag -l`.split("\n")
39
+ puts "Available tags from #{tags_source} = #{tags}" if verbose
40
+ tags.delete_if { |tag| tag.include?('-') or (tag =~ /^v/) }
41
+
42
+ if tags.empty?
43
+ puts " No tags exist from #{tags_source}"
44
+ else
45
+ last_tag = (tags.sort { |a,b| Gem::Version.new(a) <=> Gem::Version.new(b) })[-1]
46
+
47
+ # determine mission-impacting files that have changed
48
+ files_changed = `git diff tags/#{last_tag} --name-only`.strip.split("\n")
49
+ files_changed.delete_if do |file|
50
+ file[0] == '.' or file == 'Rakefile' or file =~ /^Gemfile|^spec\/|^doc\//
51
+ end
52
+
53
+ if files_changed.empty?
54
+ puts " No new tag required: No significant files have changed since '#{last_tag}' tag"
55
+ else
56
+ curr_version = Gem::Version.new(info.version)
57
+ last_tag_version = Gem::Version.new(last_tag)
58
+
59
+ if curr_version < last_tag_version
60
+ fail("ERROR: Version regression. '#{info.version}' < last tag '#{last_tag}'")
61
+ elsif curr_version == last_tag_version
62
+ fail("ERROR: Version update beyond last tag '#{last_tag}' is required for #{files_changed.count} changed files:\n * #{files_changed.join("\n * ")}")
63
+ else
64
+ puts "NOTICE: New tag of version '#{info.version}' is required for #{files_changed.count} changed files:\n * #{files_changed.join("\n * ")}"
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ # Generate an appropriate changelog for an annotated tag from a
72
+ # component's CHANGELOG or RPM spec file.
73
+ #
74
+ # The changelog is only parsed up to the first entry that fails
75
+ # validation.
76
+ #
77
+ # Fails if any of the following occur:
78
+ # - The metadata.json file for a Puppet module component cannot be
79
+ # parsed.
80
+ # - The CHANGELOG file for a Puppet module component does not exist.
81
+ # - The CHANGELOG entries for the latest version are malformed.
82
+ # - The RPM spec file for a non-Puppet module component does not exist.
83
+ # - More than 1 RPM spec file for a non-Puppet module component exists.
84
+ # - No valid changelog entries for the version specified in the
85
+ # metadata.json/spec file are found.
86
+ # - The latest changelog version is greater than the version in the
87
+ # metadata.json or the RPM spec file.
88
+ # - The RPM release specified in the spec file does not match the
89
+ # release in a changelog entry for the version.
90
+ # - Any changelog entry below the first entry has a version greater
91
+ # than that of the first entry. Changelog entries must be
92
+ # ordered from latest version to earliest version.
93
+ # - The changelog entries for the latest version are out of date
94
+ # order.
95
+ # - The weekday for a changelog entry for the latest version
96
+ # does not match the date specified.
97
+ #
98
+ # +component_dir+:: The root directory of the component project.
99
+ # +verbose+:: Whether to log non-catestrophic changelog parsing
100
+ # failures.
101
+ def self.create_tag_changelog(component_dir, verbose = false)
102
+ info, changelogs = load_and_validate_changelog(component_dir,verbose)
103
+
104
+ result = "\nRelease of #{info.version}\n"
105
+ changelogs.each do |entry|
106
+ result += "\n#{entry[:content].first}\n"
107
+ if entry[:content].size > 1
108
+ entry[:content][1..-1].each do |line|
109
+ result += " #{line}\n"
110
+ end
111
+ end
112
+ end
113
+ result
114
+ end
115
+
116
+ # Returns all changelog entries for the version
117
+ #
118
+ # Fails if
119
+ # - No valid entries for specified version are found
120
+ # - The latest changelog version is greater than the specified version
121
+ # - The release qualifier in a changelog entry for the specified
122
+ # version does not match the specified release.
123
+ #
124
+ # +changelog_entries+:: Array containing valid changelog entries, each of
125
+ # which is a Hash with :date, :version, :release, and :content keys
126
+ #
127
+ # +version+:: Target version for which one or more changelog entries
128
+ # are to be extracted
129
+ #
130
+ # +release+:: Optional release qualifier for version
131
+ # +verbose+:: Whether to log non-catestrophic changelog parsing
132
+ # failures.
133
+ def self.extract_version_changelog(changelog_entries, version,
134
+ release=nil, verbose=false)
135
+
136
+ version_entry_found = false
137
+ changelogs = []
138
+ changelog_entries.each do |entry|
139
+ if entry[:version] > version
140
+ fail("ERROR: Changelog entry for version > #{version} found: \n #{entry[:content].join("\n")}")
141
+ elsif entry[:version] < version
142
+ break
143
+ elsif entry[:version] == version
144
+ # If release is extracted from an RPM spec file, it may have
145
+ # a distribution at the end (e.g., 0.el7). We can't extract
146
+ # distribution from a specfile (query returns 'none' for
147
+ # DISTRIBUTION and DISTAG tags), so make sure the beginning
148
+ # of the release matches.
149
+ if release and entry[:release] and release.match(/^#{entry[:release]}/).nil?
150
+ fail("ERROR: Version release does not match #{release}: \n #{entry[:content].join("\n")}")
151
+ end
152
+ changelogs << entry
153
+ end
154
+ end
155
+
156
+ if changelogs.empty?
157
+ fail("ERROR: No valid changelog entry for version #{version} found")
158
+ end
159
+ changelogs
160
+ end
161
+
162
+ def self.load_and_validate_changelog(component_dir, verbose)
163
+ # only get valid changelog entries for the latest version
164
+ # (up to the first malformed entry)
165
+ info = Simp::ComponentInfo.new(component_dir, true)
166
+
167
+ changelogs = extract_version_changelog(info.changelog, info.version,
168
+ info.release, verbose)
169
+
170
+ [info, changelogs]
171
+ end
172
+ end