Class: GitHubChangelogGenerator::Generator

Inherits:
Object
  • Object
show all
Defined in:
lib/github_changelog_generator/generator/generator.rb,
lib/github_changelog_generator/generator/generator_tags.rb,
lib/github_changelog_generator/generator/generator_fetcher.rb,
lib/github_changelog_generator/generator/generator_processor.rb,
lib/github_changelog_generator/generator/generator_generation.rb

Constant Summary collapse

MAX_THREAD_NUMBER =
25

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Generator

A Generator responsible for all logic, related with change log generation from ready-to-parse issues

Example:

generator = GitHubChangelogGenerator::Generator.new
content = generator.compound_changelog


21
22
23
24
25
# File 'lib/github_changelog_generator/generator/generator.rb', line 21

def initialize(options = {})
  @options        = options
  @tag_times_hash = {}
  @fetcher        = GitHubChangelogGenerator::OctoFetcher.new(options)
end

Instance Attribute Details

#filtered_tagsObject

Returns the value of attribute filtered_tags.



14
15
16
# File 'lib/github_changelog_generator/generator/generator.rb', line 14

def filtered_tags
  @filtered_tags
end

#githubObject

Returns the value of attribute github.



14
15
16
# File 'lib/github_changelog_generator/generator/generator.rb', line 14

def github
  @github
end

#optionsObject

Returns the value of attribute options.



14
15
16
# File 'lib/github_changelog_generator/generator/generator.rb', line 14

def options
  @options
end

#sorted_tagsObject

Returns the value of attribute sorted_tags.



14
15
16
# File 'lib/github_changelog_generator/generator/generator.rb', line 14

def sorted_tags
  @sorted_tags
end

#tag_section_mappingObject

Returns the value of attribute tag_section_mapping.



14
15
16
# File 'lib/github_changelog_generator/generator/generator.rb', line 14

def tag_section_mapping
  @tag_section_mapping
end

Instance Method Details

#build_tag_section_mapping(filtered_tags, all_tags) ⇒ Hash

PRs to include in this section will be >= [Left Tag Date] and <= [Right Tag Date]

Parameters:

  • filtered_tags (Array)

    are the tags that need a subsection output

  • all_tags (Array)

    is the list of all tags ordered from newest -> oldest

Returns:

  • (Hash)

    key is the tag to output, value is an array of [Left Tag, Right Tag]



25
26
27
28
29
30
31
32
33
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 25

def build_tag_section_mapping(filtered_tags, all_tags)
  tag_mapping = {}
  filtered_tags.each do |tag|
    older_tag_idx = all_tags.index(tag) + 1
    older_tag = all_tags[older_tag_idx]
    tag_mapping[tag] = [older_tag, tag]
  end
  tag_mapping
end

#compound_changelogString

Main function to start change log generation

Returns:

  • (String)

    Generated change log file



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 7

def compound_changelog
  fetch_and_filter_tags
  fetch_issues_and_pr

  log = ""
  log += options[:frontmatter] if options[:frontmatter]
  log += "#{options[:header]}\n\n"

  log += if options[:unreleased_only]
           generate_log_between_tags(filtered_tags[0], nil)
         else
           generate_log_for_all_tags
         end

  log += File.read(options[:base]) if File.file?(options[:base])

  log += "\n\n\\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*"
  @log = log
end

#create_log_for_tag(pull_requests, issues, newer_tag, older_tag_name = nil) ⇒ String

Generates log for section with header and body

Parameters:

  • pull_requests (Array)

    List or PR’s in new section

  • issues (Array)

    List of issues in new section

  • newer_tag (String)

    Name of the newer tag. Could be nil for ‘Unreleased` section

  • older_tag_name (String) (defaults to: nil)

    Older tag, used for the links. Could be nil for last tag.

Returns:

  • (String)

    Ready and parsed section



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/github_changelog_generator/generator/generator.rb', line 60

def create_log_for_tag(pull_requests, issues, newer_tag, older_tag_name = nil)
  newer_tag_link, newer_tag_name, newer_tag_time = detect_link_tag_time(newer_tag)

  github_site = options[:github_site] || "https://github.com"
  project_url = "#{github_site}/#{options[:user]}/#{options[:project]}"

  log = generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name, project_url)

  if options[:issues]
    # Generate issues:
    log += issues_to_log(issues, pull_requests)
  end

  if options[:pulls]
    # Generate pull requests:
    log += generate_sub_section(pull_requests, options[:merge_prefix])
  end

  log
end

#delete_by_time(issues, hash_key = "actual_date", older_tag = nil, newer_tag = nil) ⇒ Array

Method filter issues, that belong only specified tag range

Parameters:

  • issues (Array)

    issues to filter

  • hash_key (Symbol) (defaults to: "actual_date")

    key of date value default is :actual_date

  • older_tag (String) (defaults to: nil)

    all issues before this tag date will be excluded. May be nil, if it’s first tag

  • newer_tag (String) (defaults to: nil)

    all issue after this tag will be excluded. May be nil for unreleased section

Returns:

  • (Array)

    filtered issues



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 71

def delete_by_time(issues, hash_key = "actual_date", older_tag = nil, newer_tag = nil)
  # in case if not tags specified - return unchanged array
  return issues if older_tag.nil? && newer_tag.nil?

  older_tag = ensure_older_tag(older_tag, newer_tag)

  newer_tag_time = newer_tag && get_time_of_tag(newer_tag)
  older_tag_time = older_tag && get_time_of_tag(older_tag)

  issues.select do |issue|
    if issue[hash_key]
      time = Time.parse(issue[hash_key].to_s).utc

      tag_in_range_old = tag_newer_old_tag?(older_tag_time, time)

      tag_in_range_new = tag_older_new_tag?(newer_tag_time, time)

      tag_in_range = tag_in_range_old && tag_in_range_new

      tag_in_range
    else
      false
    end
  end
end

#detect_actual_closed_dates(issues) ⇒ Object

Find correct closed dates, if issues was closed by commits



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/github_changelog_generator/generator/generator_fetcher.rb', line 37

def detect_actual_closed_dates(issues)
  print "Fetching closed dates for issues...\r" if options[:verbose]

  issues.each_slice(MAX_THREAD_NUMBER) do |issues_slice|
    threads = []
    issues_slice.each do |issue|
      threads << Thread.new { find_closed_date_by_commit(issue) }
    end
    threads.each(&:join)
  end
  puts "Fetching closed dates for issues: Done!" if options[:verbose]
end

#detect_due_tagObject



87
88
89
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 87

def detect_due_tag
  @due_tag ||= options.fetch(:due_tag, nil)
end

Detect link, name and time for specified tag.

Parameters:

  • newer_tag (Hash)

    newer tag. Can be nil, if it’s Unreleased section.

Returns:

  • (Array)

    link, name and time of the tag



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 66

def detect_link_tag_time(newer_tag)
  # if tag is nil - set current time
  newer_tag_time = newer_tag.nil? ? Time.new : get_time_of_tag(newer_tag)

  # if it's future release tag - set this value
  if newer_tag.nil? && options[:future_release]
    newer_tag_name = options[:future_release]
    newer_tag_link = options[:future_release]
  else
    # put unreleased label if there is no name for the tag
    newer_tag_name = newer_tag.nil? ? options[:unreleased_label] : newer_tag["name"]
    newer_tag_link = newer_tag.nil? ? "HEAD" : newer_tag_name
  end
  [newer_tag_link, newer_tag_name, newer_tag_time]
end

#detect_since_tagObject

Returns try to find newest tag using #Reader and :base option if specified otherwise returns nil.

Returns:

  • (Object)

    try to find newest tag using #Reader and :base option if specified otherwise returns nil



83
84
85
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 83

def detect_since_tag
  @since_tag ||= options.fetch(:since_tag) { version_of_first_item }
end

#encapsulate_string(string) ⇒ String

Encapsulate characters to make markdown look as expected.

Parameters:

  • string (String)

Returns:

  • (String)

    encapsulated input string



42
43
44
45
46
47
48
49
50
51
# File 'lib/github_changelog_generator/generator/generator.rb', line 42

def encapsulate_string(string)
  string.gsub! '\\', '\\\\'

  encpas_chars = %w(< > * _ \( \) [ ] #)
  encpas_chars.each do |char|
    string.gsub! char, "\\#{char}"
  end

  string
end

#ensure_older_tag(older_tag, newer_tag) ⇒ Object



97
98
99
100
101
102
103
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 97

def ensure_older_tag(older_tag, newer_tag)
  return older_tag if older_tag
  idx = sorted_tags.index { |t| t["name"] == newer_tag["name"] }
  # skip if we are already at the oldest element
  return if idx == sorted_tags.size - 1
  sorted_tags[idx - 1]
end

#exclude_issues_by_labels(issues) ⇒ Array

delete all labels with labels from options array

Parameters:

  • issues (Array)

Returns:

  • (Array)

    filtered array



7
8
9
10
11
12
13
14
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 7

def exclude_issues_by_labels(issues)
  return issues if !options[:exclude_labels] || options[:exclude_labels].empty?

  issues.reject do |issue|
    labels = issue["labels"].map { |l| l["name"] }
    (labels & options[:exclude_labels]).any?
  end
end

#fetch_and_filter_tagsObject

fetch, filter tags, fetch dates and sort them in time order



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 5

def fetch_and_filter_tags
  detect_since_tag
  detect_due_tag

  all_tags      = @fetcher.get_all_tags
  included_tags = filter_excluded_tags(all_tags)

  fetch_tags_dates(all_tags) # Creates a Hash @tag_times_hash
  @sorted_tags   = sort_tags_by_date(included_tags)
  @filtered_tags = get_filtered_tags(included_tags)

  @tag_section_mapping = build_tag_section_mapping(@filtered_tags, sorted_tags)

  @filtered_tags
end

#fetch_events_for_issues_and_prArray

Fetch event for issues and pull requests

Returns:

  • (Array)

    array of fetched issues



8
9
10
11
12
13
14
15
# File 'lib/github_changelog_generator/generator/generator_fetcher.rb', line 8

def fetch_events_for_issues_and_pr
  if options[:verbose]
    print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r"
  end

  # Async fetching events:
  @fetcher.fetch_events_async(@issues + @pull_requests)
end

#fetch_issues_and_prObject



27
28
29
30
31
32
33
34
35
36
# File 'lib/github_changelog_generator/generator/generator.rb', line 27

def fetch_issues_and_pr
  issues, pull_requests = @fetcher.fetch_closed_issues_and_pr

  @pull_requests = options[:pulls] ? get_filtered_pull_requests(pull_requests) : []

  @issues = options[:issues] ? get_filtered_issues(issues) : []

  fetch_events_for_issues_and_pr
  detect_actual_closed_dates(@issues + @pull_requests)
end

#fetch_tags_dates(tags) ⇒ Object

Async fetching of all tags dates



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/github_changelog_generator/generator/generator_fetcher.rb', line 18

def fetch_tags_dates(tags)
  print "Fetching tag dates...\r" if options[:verbose]
  # Async fetching tags:
  threads = []
  i = 0
  all = tags.count
  tags.each do |tag|
    print "                                 \r"
    threads << Thread.new do
      get_time_of_tag(tag)
      print "Fetching tags dates: #{i + 1}/#{all}\r" if options[:verbose]
      i += 1
    end
  end
  threads.each(&:join)
  puts "Fetching tags dates: #{i}" if options[:verbose]
end

#filter_array_by_labels(all_issues) ⇒ Array

General filtered function

Parameters:

  • all_issues (Array)

Returns:

  • (Array)

    filtered issues



158
159
160
161
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 158

def filter_array_by_labels(all_issues)
  filtered_issues = include_issues_by_labels(all_issues)
  exclude_issues_by_labels(filtered_issues)
end

#filter_between_tags(all_tags) ⇒ Array

Returns filtered tags according :between_tags option.

Parameters:

  • all_tags (Array)

    all tags

Returns:

  • (Array)

    filtered tags according :between_tags option



149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 149

def filter_between_tags(all_tags)
  filtered_tags = all_tags
  tag_names     = filtered_tags.map { |ft| ft["name"] }

  if options[:between_tags]
    options[:between_tags].each do |tag|
      unless tag_names.include?(tag)
        Helper.log.warn "Warning: can't find tag #{tag}, specified with --between-tags option."
      end
    end
    filtered_tags = all_tags.select { |tag| options[:between_tags].include?(tag["name"]) }
  end
  filtered_tags
end

#filter_by_include_labels(issues) ⇒ Object



143
144
145
146
147
148
149
150
151
152
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 143

def filter_by_include_labels(issues)
  if options[:include_labels].nil?
    issues
  else
    issues.select do |issue|
      labels = issue["labels"].map { |l| l["name"] } & options[:include_labels]
      labels.any?
    end
  end
end

#filter_by_milestone(filtered_issues, tag_name, all_issues) ⇒ Array

Returns filtered issues accourding milestone.

Returns:

  • (Array)

    filtered issues accourding milestone



17
18
19
20
21
22
23
24
25
26
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 17

def filter_by_milestone(filtered_issues, tag_name, all_issues)
  remove_issues_in_milestones(filtered_issues)
  unless tag_name.nil?
    # add missed issues (according milestones)
    issues_to_add = find_issues_to_add(all_issues, tag_name)

    filtered_issues |= issues_to_add
  end
  filtered_issues
end

#filter_due_tag(all_tags) ⇒ Array

Returns filtered tags according :due_tag option.

Parameters:

  • all_tags (Array)

    all tags

Returns:

  • (Array)

    filtered tags according :due_tag option



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 129

def filter_due_tag(all_tags)
  filtered_tags = all_tags
  tag           = detect_due_tag
  if tag
    if all_tags.any? && all_tags.map { |t| t["name"] }.include?(tag)
      idx = all_tags.index { |t| t["name"] == tag }
      filtered_tags = if idx > 0
                        all_tags[(idx + 1)..-1]
                      else
                        []
                      end
    else
      Helper.log.warn "Warning: can't find tag #{tag}, specified with --due-tag option."
    end
  end
  filtered_tags
end

#filter_excluded_tags(all_tags) ⇒ Array

Returns filtered tags according :exclude_tags or :exclude_tags_regex option.

Parameters:

  • all_tags (Array)

    all tags

Returns:

  • (Array)

    filtered tags according :exclude_tags or :exclude_tags_regex option



166
167
168
169
170
171
172
173
174
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 166

def filter_excluded_tags(all_tags)
  if options[:exclude_tags]
    apply_exclude_tags(all_tags)
  elsif options[:exclude_tags_regex]
    apply_exclude_tags_regex(all_tags)
  else
    all_tags
  end
end

#filter_issues_for_tags(newer_tag, older_tag) ⇒ Array

Apply all filters to issues and pull requests

Returns:

  • (Array)

    filtered issues and pull requests



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 97

def filter_issues_for_tags(newer_tag, older_tag)
  filtered_pull_requests = delete_by_time(@pull_requests, "actual_date", older_tag, newer_tag)
  filtered_issues        = delete_by_time(@issues, "actual_date", older_tag, newer_tag)

  newer_tag_name = newer_tag.nil? ? nil : newer_tag["name"]

  if options[:filter_issues_by_milestone]
    # delete excess irrelevant issues (according milestones). Issue #22.
    filtered_issues = filter_by_milestone(filtered_issues, newer_tag_name, @issues)
    filtered_pull_requests = filter_by_milestone(filtered_pull_requests, newer_tag_name, @pull_requests)
  end
  [filtered_issues, filtered_pull_requests]
end

#filter_merged_pull_requests(pull_requests) ⇒ Object

This method filter only merged PR and fetch missing required attributes for pull requests :merged_at - is a date, when issue PR was merged. More correct to use merged date, rather than closed date.



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 186

def filter_merged_pull_requests(pull_requests)
  print "Fetching merged dates...\r" if options[:verbose]
  closed_pull_requests = @fetcher.fetch_closed_pull_requests

  pull_requests.each do |pr|
    fetched_pr = closed_pull_requests.find do |fpr|
      fpr["number"] == pr["number"]
    end
    if fetched_pr
      pr["merged_at"] = fetched_pr["merged_at"]
      closed_pull_requests.delete(fetched_pr)
    end
  end

  pull_requests.select! do |pr|
    !pr["merged_at"].nil?
  end

  pull_requests
end

#filter_since_tag(all_tags) ⇒ Array

Returns filtered tags according :since_tag option.

Parameters:

  • all_tags (Array)

    all tags

Returns:

  • (Array)

    filtered tags according :since_tag option



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 109

def filter_since_tag(all_tags)
  filtered_tags = all_tags
  tag = detect_since_tag
  if tag
    if all_tags.map { |t| t["name"] }.include? tag
      idx = all_tags.index { |t| t["name"] == tag }
      filtered_tags = if idx > 0
                        all_tags[0..idx - 1]
                      else
                        []
                      end
    else
      Helper.log.warn "Warning: can't find tag #{tag}, specified with --since-tag option."
    end
  end
  filtered_tags
end

#filter_wo_labels(issues) ⇒ Array

Returns issues without labels or empty array if add_issues_wo_labels is false.

Returns:

  • (Array)

    issues without labels or empty array if add_issues_wo_labels is false



133
134
135
136
137
138
139
140
141
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 133

def filter_wo_labels(issues)
  if options[:add_issues_wo_labels]
    issues_wo_labels = issues.select do |issue|
      !issue["labels"].map { |l| l["name"] }.any?
    end
    return issues_wo_labels
  end
  []
end

#find_closed_date_by_commit(issue) ⇒ Object

Fill :actual_date parameter of specified issue by closed date of the commit, if it was closed by commit.

Parameters:

  • issue (Hash)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/github_changelog_generator/generator/generator_fetcher.rb', line 52

def find_closed_date_by_commit(issue)
  unless issue["events"].nil?
    # if it's PR -> then find "merged event", in case of usual issue -> fond closed date
    compare_string = issue["merged_at"].nil? ? "closed" : "merged"
    # reverse! - to find latest closed event. (event goes in date order)
    issue["events"].reverse!.each do |event|
      if event["event"].eql? compare_string
        set_date_from_event(event, issue)
        break
      end
    end
  end
  # TODO: assert issues, that remain without 'actual_date' hash for some reason.
end

#find_issues_to_add(all_issues, tag_name) ⇒ Array

Add all issues, that should be in that tag, according milestone

Parameters:

  • all_issues (Array)
  • tag_name (String)

Returns:

  • (Array)

    issues with milestone #tag_name



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 33

def find_issues_to_add(all_issues, tag_name)
  all_issues.select do |issue|
    if issue["milestone"].nil?
      false
    else
      # check, that this milestone in tag list:
      milestone_is_tag = @filtered_tags.find do |tag|
        tag["name"] == issue["milestone"]["title"]
      end

      if milestone_is_tag.nil?
        false
      else
        issue["milestone"]["title"] == tag_name
      end
    end
  end
end

#generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_link, project_url) ⇒ String

It generate one header for section with specific parameters.

Parameters:

  • newer_tag_name (String)
    • name of newer tag

  • newer_tag_link (String)
    • used for links. Could be same as #newer_tag_name or some specific value, like HEAD

  • newer_tag_time (Time)
    • time, when newer tag created

  • older_tag_link (String)
    • tag name, used for links.

  • project_url (String)
    • url for current project.

Returns:

  • (String)
    • Generate one ready-to-add section.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 52

def generate_header(newer_tag_name, newer_tag_link, newer_tag_time, older_tag_link, project_url)
  log = ""

  # Generate date string:
  time_string = newer_tag_time.strftime(options[:date_format])

  # Generate tag name and link
  release_url = if options[:release_url]
                  format(options[:release_url], newer_tag_link)
                else
                  "#{project_url}/tree/#{newer_tag_link}"
                end
  log += if newer_tag_name.equal?(options[:unreleased_label])
           "## [#{newer_tag_name}](#{release_url})\n\n"
         else
           "## [#{newer_tag_name}](#{release_url}) (#{time_string})\n"
         end

  if options[:compare_link] && older_tag_link
    # Generate compare link
    log += "[Full Changelog](#{project_url}/compare/#{older_tag_link}...#{newer_tag_link})\n\n"
  end

  log
end

#generate_log_between_tags(older_tag, newer_tag) ⇒ Object

Generate log only between 2 specified tags

Parameters:

  • older_tag (String)

    all issues before this tag date will be excluded. May be nil, if it’s first tag

  • newer_tag (String)

    all issue after this tag will be excluded. May be nil for unreleased section



81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 81

def generate_log_between_tags(older_tag, newer_tag)
  filtered_issues, filtered_pull_requests = filter_issues_for_tags(newer_tag, older_tag)

  older_tag_name = older_tag.nil? ? detect_since_tag : older_tag["name"]

  if newer_tag.nil? && filtered_issues.empty? && filtered_pull_requests.empty?
    # do not generate empty unreleased section
    return ""
  end

  create_log_for_tag(filtered_pull_requests, filtered_issues, newer_tag, older_tag_name)
end

#generate_log_for_all_tagsString

The full cycle of generation for whole project

Returns:

  • (String)

    The complete change log



113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 113

def generate_log_for_all_tags
  puts "Generating log..." if options[:verbose]

  log = generate_unreleased_section

  @tag_section_mapping.each_pair do |_tag_section, left_right_tags|
    older_tag, newer_tag = left_right_tags
    log += generate_log_between_tags(older_tag, newer_tag)
  end

  log
end

#generate_sub_section(issues, prefix) ⇒ String

Returns Generate ready-to-go sub-section.

Parameters:

  • issues (Array)

    List of issues on sub-section

  • prefix (String)

    Name of sub-section

Returns:

  • (String)

    Generate ready-to-go sub-section



30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 30

def generate_sub_section(issues, prefix)
  log = ""

  if issues.any?
    log += "#{prefix}\n\n" unless options[:simple_list]
    issues.each do |issue|
      merge_string = get_string_for_issue(issue)
      log += "- #{merge_string}\n"
    end
    log += "\n"
  end
  log
end

#generate_unreleased_sectionObject



126
127
128
129
130
131
132
133
134
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 126

def generate_unreleased_section
  log = ""
  if options[:unreleased]
    start_tag      = filtered_tags[0] || sorted_tags.last
    unreleased_log = generate_log_between_tags(start_tag, nil)
    log           += unreleased_log if unreleased_log
  end
  log
end

#get_filtered_issues(issues) ⇒ Array

Filter issues according labels

Returns:

  • (Array)

    Filtered issues



165
166
167
168
169
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 165

def get_filtered_issues(issues)
  issues = filter_array_by_labels(issues)
  puts "Filtered issues: #{issues.count}" if options[:verbose]
  issues
end

#get_filtered_pull_requests(pull_requests) ⇒ Array

This method fetches missing params for PR and filter them by specified options It include add all PR’s with labels from options array And exclude all from :exclude_labels array.

Returns:

  • (Array)

    filtered PR’s



175
176
177
178
179
180
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 175

def get_filtered_pull_requests(pull_requests)
  pull_requests = filter_array_by_labels(pull_requests)
  pull_requests = filter_merged_pull_requests(pull_requests)
  puts "Filtered pull requests: #{pull_requests.count}" if options[:verbose]
  pull_requests
end

#get_filtered_tags(all_tags) ⇒ Array

Return tags after filtering tags in lists provided by option: –between-tags & –exclude-tags

Returns:

  • (Array)


101
102
103
104
105
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 101

def get_filtered_tags(all_tags)
  filtered_tags = filter_since_tag(all_tags)
  filtered_tags = filter_due_tag(filtered_tags)
  filter_between_tags(filtered_tags)
end

#get_string_for_issue(issue) ⇒ String

Parse issue and generate single line formatted issue line.

Example output:

Parameters:

  • issue (Hash)

    Fetched issue from GitHub

Returns:

  • (String)

    Markdown-formatted single issue



143
144
145
146
147
148
149
150
151
# File 'lib/github_changelog_generator/generator/generator_generation.rb', line 143

def get_string_for_issue(issue)
  encapsulated_title = encapsulate_string issue["title"]

  title_with_number = "#{encapsulated_title} [\\##{issue['number']}](#{issue['html_url']})"
  if options[:issue_line_labels].present?
    title_with_number = "#{title_with_number}#{line_labels_for(issue)}"
  end
  issue_line_with_user(title_with_number, issue)
end

#get_time_of_tag(tag_name) ⇒ Time

Returns date for given GitHub Tag hash

Memoize the date by tag name.

Parameters:

  • tag_name (Hash)

Returns:

  • (Time)

    time of specified tag

Raises:



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 50

def get_time_of_tag(tag_name)
  raise ChangelogGeneratorError, "tag_name is nil" if tag_name.nil?

  name_of_tag = tag_name.fetch("name")
  time_for_tag_name = @tag_times_hash[name_of_tag]
  return time_for_tag_name if time_for_tag_name

  @fetcher.fetch_date_of_tag(tag_name).tap do |time_string|
    @tag_times_hash[name_of_tag] = time_string
  end
end

#include_issues_by_labels(issues) ⇒ Array

Include issues with labels, specified in :include_labels

Parameters:

  • issues (Array)

    to filter

Returns:

  • (Array)

    filtered array of issues



126
127
128
129
130
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 126

def include_issues_by_labels(issues)
  filtered_issues = filter_by_include_labels(issues)
  filtered_issues |= filter_wo_labels(issues)
  filtered_issues
end

#issues_to_log(issues, pull_requests) ⇒ String

Generate ready-to-paste log from list of issues and pull requests.

Parameters:

  • issues (Array)
  • pull_requests (Array)

Returns:

  • (String)

    generated log for issues



86
87
88
89
90
91
92
93
94
# File 'lib/github_changelog_generator/generator/generator.rb', line 86

def issues_to_log(issues, pull_requests)
  log = ""
  bugs_a, enhancement_a, issues_a = parse_by_sections(issues, pull_requests)

  log += generate_sub_section(enhancement_a, options[:enhancement_prefix])
  log += generate_sub_section(bugs_a, options[:bug_prefix])
  log += generate_sub_section(issues_a, options[:issue_prefix])
  log
end

#parse_by_sections(issues, pull_requests) ⇒ Array

This method sort issues by types (bugs, features, or just closed issues) by labels

Parameters:

  • issues (Array)
  • pull_requests (Array)

Returns:

  • (Array)

    tuple of filtered arrays: (Bugs, Enhancements Issues)



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/github_changelog_generator/generator/generator.rb', line 102

def parse_by_sections(issues, pull_requests)
  issues_a = []
  enhancement_a = []
  bugs_a = []

  issues.each do |dict|
    added = false
    dict["labels"].each do |label|
      if options[:bug_labels].include?(label["name"])
        bugs_a.push(dict)
        added = true
        next
      end
      if options[:enhancement_labels].include?(label["name"])
        enhancement_a.push(dict)
        added = true
        next
      end
    end
    issues_a.push(dict) unless added
  end

  added_pull_requests = []
  pull_requests.each do |pr|
    pr["labels"].each do |label|
      if options[:bug_labels].include?(label["name"])
        bugs_a.push(pr)
        added_pull_requests.push(pr)
        next
      end
      if options[:enhancement_labels].include?(label["name"])
        enhancement_a.push(pr)
        added_pull_requests.push(pr)
        next
      end
    end
  end
  added_pull_requests.each { |p| pull_requests.delete(p) }

  [bugs_a, enhancement_a, issues_a]
end

#remove_issues_in_milestones(filtered_issues) ⇒ Array

Returns array with removed issues, that contain milestones with same name as a tag.

Returns:

  • (Array)

    array with removed issues, that contain milestones with same name as a tag



53
54
55
56
57
58
59
60
61
62
63
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 53

def remove_issues_in_milestones(filtered_issues)
  filtered_issues.select! do |issue|
    # leave issues without milestones
    if issue["milestone"].nil?
      true
    else
      # check, that this milestone in tag list:
      @filtered_tags.find { |tag| tag["name"] == issue["milestone"]["title"] }.nil?
    end
  end
end

#set_date_from_event(event, issue) ⇒ Object

Set closed date from this issue

Parameters:

  • event (Hash)
  • issue (Hash)


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/github_changelog_generator/generator/generator_fetcher.rb', line 71

def set_date_from_event(event, issue)
  if event["commit_id"].nil?
    issue["actual_date"] = issue["closed_at"]
  else
    begin
      commit = @fetcher.fetch_commit(event)
      issue["actual_date"] = commit["commit"]["author"]["date"]

      # issue['actual_date'] = commit['author']['date']
    rescue
      puts "Warning: Can't fetch commit #{event['commit_id']}. It is probably referenced from another repo."
      issue["actual_date"] = issue["closed_at"]
    end
  end
end

#sort_tags_by_date(tags) ⇒ Object

Sort all tags by date, newest to oldest



36
37
38
39
40
41
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 36

def sort_tags_by_date(tags)
  puts "Sorting tags..." if options[:verbose]
  tags.sort_by! do |x|
    get_time_of_tag(x)
  end.reverse!
end

#tag_newer_old_tag?(older_tag_time, t) ⇒ Boolean

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 114

def tag_newer_old_tag?(older_tag_time, t)
  tag_in_range_old = if older_tag_time.nil?
                       true
                     else
                       t > older_tag_time
                     end
  tag_in_range_old
end

#tag_older_new_tag?(newer_tag_time, time) ⇒ Boolean

Returns:

  • (Boolean)


105
106
107
108
109
110
111
112
# File 'lib/github_changelog_generator/generator/generator_processor.rb', line 105

def tag_older_new_tag?(newer_tag_time, time)
  tag_in_range_new = if newer_tag_time.nil?
                       true
                     else
                       time <= newer_tag_time
                     end
  tag_in_range_new
end

#version_of_first_itemObject



91
92
93
94
95
96
# File 'lib/github_changelog_generator/generator/generator_tags.rb', line 91

def version_of_first_item
  return unless File.file?(options[:base].to_s)

  sections = GitHubChangelogGenerator::Reader.new.read(options[:base])
  sections.first["version"] if sections && sections.any?
end