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.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/CHANGELOG.md +57 -43
- data/CODE_OF_CONDUCT.md +1 -1
- data/LICENSE.txt +1 -1
- data/lib/release/notes.rb +13 -6
- data/lib/release/notes/commits.rb +141 -0
- data/lib/release/notes/configurable.rb +16 -0
- data/lib/release/notes/date_formatter.rb +31 -0
- data/lib/release/notes/git.rb +60 -25
- data/lib/release/notes/link.rb +55 -13
- data/lib/release/notes/log.rb +71 -103
- data/lib/release/notes/prettify.rb +39 -0
- data/lib/release/notes/system.rb +58 -25
- data/lib/release/notes/tag.rb +152 -0
- data/lib/release/notes/version.rb +1 -1
- data/lib/release/notes/write.rb +86 -30
- metadata +7 -4
- data/lib/release/notes/date_format.rb +0 -19
- data/lib/release/notes/pretty_print.rb +0 -24
data/lib/release/notes/git.rb
CHANGED
@@ -2,41 +2,76 @@
|
|
2
2
|
|
3
3
|
module Release
|
4
4
|
module Notes
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
" #{
|
18
|
-
" #{
|
23
|
+
" #{config_regex_type} #{config_grep_insensitive?}" \
|
24
|
+
" #{config_include_merges?} --format='%h #{log_format}'"
|
19
25
|
end
|
20
|
-
end
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
39
|
-
|
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
|
data/lib/release/notes/link.rb
CHANGED
@@ -6,8 +6,15 @@ module Release
|
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
8
|
included do
|
9
|
-
|
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
|
-
#
|
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(
|
22
|
-
unless
|
23
|
-
@new_lines += "#{line}
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
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)}
|
69
|
+
@new_lines += "#{replace(line, @word, label, index)}#{NEWLINE}" if @word
|
43
70
|
end
|
44
71
|
|
45
|
-
#
|
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
|
-
#
|
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 = "#{
|
58
|
-
linked = "[#{humanized}](#{
|
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
|
data/lib/release/notes/log.rb
CHANGED
@@ -3,140 +3,108 @@
|
|
3
3
|
module Release
|
4
4
|
module Notes
|
5
5
|
class Log
|
6
|
-
include
|
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
|
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
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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
|
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
|
-
#
|
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
|
-
|
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
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
#
|
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(
|
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
|
-
#
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
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
|
-
#
|
128
|
-
|
129
|
-
|
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
|
-
#
|
133
|
-
|
134
|
-
|
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
|
-
#
|
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 ||=
|
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
|