release-notes 2.0.0 → 3.0.0

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.
@@ -2,41 +2,76 @@
2
2
 
3
3
  module Release
4
4
  module Notes
5
- module Git
6
- module_function
7
-
8
- extend ActiveSupport::Concern
9
-
10
- included do
11
- delegate :all_labels, :grep_insensitive?,
12
- :regex_type, :include_merges?, to: :"Release::Notes.configuration"
5
+ class Git
6
+ class << self
7
+ include Configurable
8
+ DEFAULT_TAG = "HEAD"
13
9
 
10
+ #
11
+ # Returns a string matching the following format: "hash - message\n"
12
+ # for all commits falling within the tag_from and tag_to threshold
13
+ # taking account the relevant label, invert grep options, and log format
14
+ #
15
+ # @param **opts
16
+ #
17
+ # @return [String] git log entries between tag_from and tag_to that include the word(s) in label,
18
+ # taking into account the invert grep and log_format flags specified.
19
+ #
14
20
  def log(**opts)
15
21
  "git log '#{opts[:tag_from]}'..'#{opts[:tag_to]}'" \
16
22
  " --grep='#{opts[:label]}#{opts[:invert_grep]}'" \
17
- " #{regex_type} #{grep_insensitive?}" \
18
- " #{include_merges?} --format='%h #{log_format}'"
23
+ " #{config_regex_type} #{config_grep_insensitive?}" \
24
+ " #{config_include_merges?} --format='%h #{log_format}'"
19
25
  end
20
- end
21
26
 
22
- def log_format
23
- "- %s"
24
- end
27
+ #
28
+ # Returns the git hash of the first commit.
29
+ #
30
+ # @return [String] the first commit hash.
31
+ #
32
+ def first_commit
33
+ "git rev-list --max-parents=0 #{DEFAULT_TAG}"
34
+ end
25
35
 
26
- def first_commit
27
- "git rev-list --max-parents=0 HEAD"
28
- end
36
+ #
37
+ # Returns the latest git tag.
38
+ #
39
+ # @return [String] the most recent tag.
40
+ #
41
+ def last_tag
42
+ "git describe --abbrev=0 --tags"
43
+ end
29
44
 
30
- def last_tag
31
- "git describe --abbrev=0 --tags"
32
- end
45
+ #
46
+ # Returns the date and time of the latest tag.
47
+ #
48
+ # @param [String] a git tag
49
+ #
50
+ # @return [String] the most recent tag date.
51
+ #
52
+ def tag_date(tag)
53
+ "git log -1 --format=%ai #{tag}"
54
+ end
33
55
 
34
- def tag_date(tag)
35
- "git log -1 --format=%ai #{tag}"
36
- end
56
+ #
57
+ # Returns an array of all tags in the git log
58
+ #
59
+ # @return [Array] all git tags
60
+ #
61
+ def read_all_tags
62
+ "git for-each-ref --sort=taggerdate --format='%(tag)' refs/tags"
63
+ end
64
+
65
+ private
37
66
 
38
- def read_all_tags
39
- "git tag | sort -u -r"
67
+ #
68
+ # Returns a string representing the git log format flag
69
+ #
70
+ # @return [String] git log format flag
71
+ #
72
+ def log_format
73
+ "- %s"
74
+ end
40
75
  end
41
76
  end
42
77
  end
@@ -6,8 +6,15 @@ module Release
6
6
  extend ActiveSupport::Concern
7
7
 
8
8
  included do
9
- delegate :link_to_labels, :link_to_sites, :link_to_humanize, to: :"Release::Notes.configuration"
9
+ include Configurable
10
10
 
11
+ #
12
+ # Link log messages
13
+ #
14
+ # @param [String] lines - log messages for a git tag
15
+ #
16
+ # @return [String] log messages that can be linked
17
+ #
11
18
  def link_lines(lines:)
12
19
  @new_lines = ""
13
20
  split_lines(lines)
@@ -16,33 +23,59 @@ module Release
16
23
 
17
24
  private
18
25
 
19
- # @api private
26
+ #
27
+ # Format lines or add link if log message should be linked
28
+ #
29
+ # @param [String] lines - log messages for a given git commit
30
+ #
31
+ # @return [Array] label log messages should be linked to
32
+ #
20
33
  def split_lines(lines)
21
- lines.split("\n").each do |line|
22
- unless link_to_labels&.any? { |la| line.include? la }
23
- @new_lines += "#{line}\n"
34
+ lines.split(NEWLINE).each do |line|
35
+ unless config_link_to_labels&.any? { |la| line.include? la }
36
+ @new_lines += "#{line}#{NEWLINE}"
24
37
  next
25
38
  end
26
39
  split_words(line)
27
40
  end
28
41
  end
29
42
 
30
- # @api private
43
+ #
44
+ # Determine if log message has a pre-determined label
45
+ #
46
+ # @param [String] line - a line from the log messages
47
+ #
48
+ # @return none
49
+ #
31
50
  def split_words(line)
32
- link_to_labels.each_with_index do |label, i|
51
+ config_link_to_labels.each_with_index do |label, i|
33
52
  next unless line.include? label
34
53
 
35
54
  replace_lines(line, label, i)
36
55
  end
37
56
  end
38
57
 
39
- # @api private
58
+ #
59
+ # Replace a word in the changelog
60
+ #
61
+ # @param [String line - a line from the log messages
62
+ # @param [String] label - a specified label
63
+ # @param [Integer] index - index of log message
64
+ #
65
+ # @return none
66
+ #
40
67
  def replace_lines(line, label, index)
41
68
  replace_words(line.split(/\s/))
42
- @new_lines += "#{replace(line, @word, label, index)}\n" if @word
69
+ @new_lines += "#{replace(line, @word, label, index)}#{NEWLINE}" if @word
43
70
  end
44
71
 
45
- # @api private
72
+ #
73
+ # Replace words if log message
74
+ #
75
+ # @param [Array] words - split git log message
76
+ #
77
+ # @return [String] word to replace in the log message
78
+ #
46
79
  def replace_words(words)
47
80
  words.each do |word|
48
81
  next unless (word =~ /^#.*/)&.zero?
@@ -51,11 +84,20 @@ module Release
51
84
  end
52
85
  end
53
86
 
54
- # @api private
87
+ #
88
+ # Replace log messages with linked messages
89
+ #
90
+ # @param [String] line - log message to replace
91
+ # @param [String] issue_number - word to replace
92
+ # @param [String] label - label to replace with
93
+ # @param [Integer] index - index of the linked site
94
+ #
95
+ # @return [String] formatted linked line
96
+ #
55
97
  def replace(line, issue_number, label, index)
56
98
  identifier = "#{label.split(/\s/)[0]} #{issue_number}"
57
- humanized = "#{link_to_humanize[index]} #{issue_number}"
58
- linked = "[#{humanized}](#{link_to_sites[index]}\/#{issue_number.tr('^0-9', '')})"
99
+ humanized = "#{config_link_to_humanize[index]} #{issue_number}"
100
+ linked = "[#{humanized}](#{config_link_to_sites[index]}\/#{issue_number.tr('^0-9', '')})"
59
101
 
60
102
  line.gsub! identifier, linked
61
103
  line
@@ -3,140 +3,108 @@
3
3
  module Release
4
4
  module Notes
5
5
  class Log
6
- include System
7
- delegate :force_rewrite, :all_labels, :log_all, :header_title,
8
- :header_title_type, :features, :bugs, :misc, :feature_title,
9
- :bug_title, :misc_title, :log_all_title, :single_label,
10
- :release_notes_exist?, to: :"Release::Notes.configuration"
11
-
12
- delegate :date_humanized, :format_tag_date, to: :date_formatter
13
- delegate :digest_header, :digest_title, to: :writer
14
-
15
- def initialize
16
- @_commits = []
17
- end
6
+ include Configurable
18
7
 
8
+ #
9
+ # Release::Notes::Log initializer
10
+ #
11
+ # @return none
12
+ #
19
13
  def perform
20
- if release_notes_exist? && !force_rewrite
21
- # Find the last tag and group all commits
22
- # under a date header at the time this is run
23
- find_last_tag_and_log
24
- else
14
+ if log_from_start?
25
15
  # Find all tags and get the logs between each tag
26
16
  # run this the first time if nothing exists
27
17
  find_all_tags_and_log_all
18
+ else
19
+ # Find the last tag and group all commits
20
+ # under a date header at the time this is run
21
+ find_last_tag_and_log
28
22
  end
23
+
29
24
  writer.write_new_file
30
25
  end
31
26
 
32
27
  private
33
28
 
34
- # @api private
35
- def copy_single_tag_of_activity(tag_from:, tag_to: "HEAD")
36
- [features, bugs, misc].each_with_index do |regex, i|
37
- log = system_log(tag_from: tag_from, tag_to: tag_to, label: regex, log_all: log_all)
38
- log_grouped_commits(title: titles[i], log: log)
39
- end
40
-
41
- return unless log_all
42
-
43
- log = system_log(tag_from: tag_from, tag_to: tag_to)
44
- log_grouped_commits(title: log_all_title, log: log)
45
- end
46
-
47
- # @api private
48
- def date_formatter
49
- @date_formatter ||= Release::Notes::DateFormat.new
50
- end
51
-
52
- # @api private
29
+ #
30
+ # Find the most recent git tag
31
+ #
32
+ # @return [Array] most recent git tag
33
+ #
53
34
  def find_last_tag_and_log
54
- last_tag = system_last_tag.strip
55
- # return false unless system_log(tag_from: last_tag, label: all_labels).present?
56
- if system_log(tag_from: last_tag, label: all_labels).blank?
57
- log_last
58
- return
59
- end
60
- # output the date right now
61
- header_content date: date_humanized, tag: tag_to
62
- copy_single_tag_of_activity(tag_from: last_tag)
35
+ tag_logger(System.last_tag.strip, previous_tag(0))
63
36
  end
64
37
 
65
- # @api private
38
+ #
39
+ # Get all git tags and add to the changelog
40
+ #
41
+ # @return [Array] all the git tags
42
+ #
66
43
  def find_all_tags_and_log_all
67
44
  git_all_tags.each_with_index do |ta, i|
68
- header_content(
69
- date: date_humanized(date: System.tag_date(tag: ta)),
70
- tag: ta,
71
- )
72
-
73
- copy_single_tag_of_activity(
74
- tag_from: previous_tag(i).strip,
75
- tag_to: ta,
76
- )
45
+ tag_logger(ta, previous_tag(i))
77
46
  end
78
47
  end
79
48
 
80
- # @api private
81
- def log_last
82
- header_content date: date_humanized, tag: git_all_tags[0]
83
- copy_single_tag_of_activity(tag_from: git_all_tags[1], tag_to: git_all_tags[0])
49
+ #
50
+ # Check whether to start from the beginning of the git log
51
+ #
52
+ # @return [Boolean] append to changelog or start from beginning of git log
53
+ #
54
+ def log_from_start?
55
+ !config_release_notes_exist? || config_force_rewrite
84
56
  end
85
57
 
86
- # @api private
58
+ #
59
+ # All tags in the git log
60
+ #
61
+ # @return [Array] all git tags
62
+ #
87
63
  def git_all_tags
88
- @git_all_tags ||= System.all_tags.split("\n")
89
- end
90
-
91
- # @api private
92
- def header_content(**date_and_tag)
93
- digest_header(date_and_tag[header_title_type.to_sym])
64
+ @git_all_tags ||= System.all_tags.split(NEWLINE).reverse
94
65
  end
95
66
 
96
- # @api private
97
- def log_grouped_commits(log:, title:)
98
- return unless log.present?
99
-
100
- log_messages = log.split("\n").map { |x| x.split(/(?=-)/) }
101
- commit_hashes = log_messages.flat_map { |msg| msg[0].strip }
102
-
103
- trimmed_commit_hashes = trim_commit_hashes(commit_hashes)
104
-
105
- digest_unique_messages(log_messages, trimmed_commit_hashes, title) if trimmed_commit_hashes.present?
106
- end
107
-
108
- # @api private
109
- def digest_unique_messages(log_messages, commit_hashes, title)
110
- messages = log_messages.map do |msg|
111
- msg[1..-1].join if commit_hashes.include?(msg[0].strip)
112
- end.compact
113
-
114
- @_commits += commit_hashes
115
-
116
- digest_title(title: title, log_message: "#{messages.join("\n")}\n")
117
- end
118
-
119
- # @api private
120
- def trim_commit_hashes(commit_hashes)
121
- commit_hashes.dup.each do |commit|
122
- commit_hashes.delete commit if single_label && @_commits.include?(commit)
123
- end
124
- commit_hashes
67
+ #
68
+ # Second to last tag in the git log
69
+ #
70
+ # @param [Integer] index - index of the git tags array
71
+ #
72
+ # @return [String] second most recent git tag
73
+ #
74
+ def previous_tag(index)
75
+ git_all_tags[index + 1].yield_self { |t| tag(t) }.strip
125
76
  end
126
77
 
127
- # @api private
128
- def previous_tag(index)
129
- git_all_tags[index + 1].present? ? git_all_tags[index + 1] : System.first_commit
78
+ #
79
+ # Get the next git tag or the first tag
80
+ #
81
+ # @param [String] git_tag - a git tag
82
+ #
83
+ # @return [String] most recent git tag or the first git tag
84
+ #
85
+ def tag(git_tag)
86
+ git_tag.present? ? git_tag : System.first_commit
130
87
  end
131
88
 
132
- # @api private
133
- def titles
134
- [feature_title, bug_title, misc_title]
89
+ #
90
+ # Create a Release::Notes::Tag object
91
+ #
92
+ # @param [String] tag - tag to
93
+ # @param [String] previous_tag - tag from
94
+ #
95
+ # @return [Object] Release::Notes::Tag object
96
+ #
97
+ def tag_logger(tag, previous_tag)
98
+ Tag.new(tag: tag, previous_tag: previous_tag, writer: writer).perform
135
99
  end
136
100
 
137
- # @api private
101
+ #
102
+ # Create write object containing the header, title, and log messages for a given tag
103
+ #
104
+ # @return [Object] Release::Notes::Write object
105
+ #
138
106
  def writer
139
- @writer ||= Release::Notes::Write.new
107
+ @writer ||= Write.new
140
108
  end
141
109
  end
142
110
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ class Prettify
6
+ include Configurable
7
+ attr_reader :line
8
+
9
+ #
10
+ # Release::Notes::Prettify initializer
11
+ #
12
+ # @param [String] line - a line from the git log
13
+ #
14
+ def initialize(line:)
15
+ @line = line
16
+ end
17
+
18
+ #
19
+ # Perform method for Release::Notes::Prettify
20
+ #
21
+ # @return [String] log message
22
+ #
23
+ def perform
24
+ line.gsub(labels_regex, "").strip
25
+ end
26
+
27
+ private
28
+
29
+ #
30
+ # Holds the regular expression used to match a pattern against labels
31
+ #
32
+ # @return [Regexp] regex containing all labels
33
+ #
34
+ def labels_regex
35
+ Regexp.new config_all_labels, Regexp::IGNORECASE
36
+ end
37
+ end
38
+ end
39
+ end