release-notes 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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