class GitHubChangelogGenerator::Generator

This class is the high-level code for gathering issues and PRs for a github repository and generating a CHANGELOG.md file. A changelog is made up of a series of “entries” of all tagged releases, plus an extra entry for the unreleased changes. Entries are made up of various organizational “sections,” and sections contain the github issues and PRs.

So the changelog contains entries, entries contain sections, and sections contain issues and PRs.

@see GitHubChangelogGenerator::Entry @see GitHubChangelogGenerator::Section

Constants

CREDIT_LINE

Attributes

filtered_tags[RW]
options[RW]
sorted_tags[RW]
tag_section_mapping[RW]

Public Class Methods

new(options = {}) click to toggle source

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

Example:

generator = GitHubChangelogGenerator::Generator.new
content = generator.compound_changelog
# File lib/github_changelog_generator/generator/generator.rb, line 40
def initialize(options = {})
  @options        = options
  @tag_times_hash = {}
  @fetcher        = GitHubChangelogGenerator::OctoFetcher.new(options)
  @sections       = []
end

Public Instance Methods

add_first_occurring_tag_to_prs(tags, prs) click to toggle source

Adds a key “first_occurring_tag” to each PR with a value of the oldest tag that a PR's merge commit occurs in in the git history. This should indicate the release of each PR by git's history regardless of dates and divergent branches.

@param [Array] tags The tags sorted by time, newest to oldest. @param [Array] prs The PRs to discover the tags of. @return [Nil] No return; PRs are updated in-place.

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 45
def add_first_occurring_tag_to_prs(tags, prs)
  total = prs.count

  prs_left = associate_tagged_prs(tags, prs, total)
  prs_left = associate_release_branch_prs(prs_left, total)
  prs_left = associate_rebase_comment_prs(tags, prs_left, total) if prs_left.any?
  # PRs in prs_left will be untagged, not in release branch, and not
  # rebased. They should not be included in the changelog as they probably
  # have been merged to a branch other than the release branch.
  @pull_requests -= prs_left
  Helper.log.info "Associating PRs with tags: #{total}/#{total}"
end
associate_rebase_comment_prs(tags, prs_left, total) click to toggle source

Associate merged PRs by the SHA detected in github comments of the form “rebased commit: <sha>”. For use when the merge_commit_sha is not in the actual git history due to rebase.

@param [Array] tags The tags sorted by time, newest to oldest. @param [Array] prs_left The PRs not yet associated with any tag or branch. @return [Array] PRs without rebase comments.

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 126
def associate_rebase_comment_prs(tags, prs_left, total)
  i = total - prs_left.count
  # Any remaining PRs were not found in the list of tags by their merge
  # commit and not found in any specified release branch. Fallback to
  # rebased commit comment.
  @fetcher.fetch_comments_async(prs_left)
  prs_left.reject do |pr|
    found = false
    if pr["comments"] && (rebased_comment = pr["comments"].reverse.find { |c| c["body"].match(%r{rebased commit: ([0-9a-f]{40})}i) })
      rebased_sha = rebased_comment["body"].match(%r{rebased commit: ([0-9a-f]{40})}i)[1]
      if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(rebased_sha) })
        pr["first_occurring_tag"] = oldest_tag["name"]
        found = true
        i += 1
      elsif sha_in_release_branch?(rebased_sha)
        found = true
        i += 1
      else
        raise StandardError, "PR #{pr['number']} has a rebased SHA comment but that SHA was not found in the release branch or any tags"
      end
      print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose]
    else
      puts "Warning: PR #{pr['number']} merge commit was not found in the release branch or tagged git history and no rebased SHA comment was found"
    end
    found
  end
end
associate_release_branch_prs(prs_left, total) click to toggle source

Associate merged PRs by the HEAD of the release branch. If no –release-branch was specified, then the github default branch is used.

@param [Array] prs_left PRs not associated with any tag. @param [Integer] total The total number of PRs to associate; used for verbose printing. @return [Array] PRs without their merge_commit_sha in the branch.

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 102
def associate_release_branch_prs(prs_left, total)
  if prs_left.any?
    i = total - prs_left.count
    prs_left.reject do |pr|
      found = false
      if pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" }) && sha_in_release_branch?(event["commit_id"])
        found = true
        i += 1
        print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose]
      end
      found
    end
  else
    prs_left
  end
end
associate_tagged_prs(tags, prs, total) click to toggle source

Associate merged PRs by the merge SHA contained in each tag. If the merge_commit_sha is not found in any tag's history, skip association.

@param [Array] tags The tags sorted by time, newest to oldest. @param [Array] prs The PRs to associate. @return [Array] PRs without their merge_commit_sha in a tag.

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 64
def associate_tagged_prs(tags, prs, total)
  @fetcher.fetch_tag_shas(tags)

  i = 0
  prs.reject do |pr|
    found = false
    # XXX Wish I could use merge_commit_sha, but gcg doesn't currently
    # fetch that. See
    # https://developer.github.com/v3/pulls/#get-a-single-pull-request vs.
    # https://developer.github.com/v3/pulls/#list-pull-requests
    if pr["events"] && (event = pr["events"].find { |e| e["event"] == "merged" })
      # Iterate tags.reverse (oldest to newest) to find first tag of each PR.
      if (oldest_tag = tags.reverse.find { |tag| tag["shas_in_tag"].include?(event["commit_id"]) })
        pr["first_occurring_tag"] = oldest_tag["name"]
        found = true
        i += 1
        print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose]
      end
    else
      # Either there were no events or no merged event. Github's api can be
      # weird like that apparently. Check for a rebased comment before erroring.
      no_events_pr = associate_rebase_comment_prs(tags, [pr], total)
      raise StandardError, "No merge sha found for PR #{pr['number']} via the GitHub API" unless no_events_pr.empty?

      found = true
      i += 1
      print("Associating PRs with tags: #{i}/#{total}\r") if @options[:verbose]
    end
    found
  end
end
build_tag_section_mapping(section_tags, filtered_tags) click to toggle source

@param [Array] section_tags are the tags that need a subsection output @param [Array] filtered_tags is the list of filtered tags ordered from newest -> oldest @return [Hash] key is the tag to output, value is an array of [Left Tag, Right Tag] PRs to include in this section will be >= [Left Tag Date] and <= [Right Tag Date] rubocop:disable Style/For - for allows us to be more concise

# File lib/github_changelog_generator/generator/generator_tags.rb, line 27
def build_tag_section_mapping(section_tags, filtered_tags)
  tag_mapping = {}
  for i in 0..(section_tags.length - 1)
    tag = section_tags[i]

    # Don't create section header for the "since" tag
    next if since_tag && tag["name"] == since_tag

    # Don't create a section header for the first tag in between_tags
    next if options[:between_tags] && tag == section_tags.last

    # Don't create a section header for excluded tags
    next unless filtered_tags.include?(tag)

    older_tag = section_tags[i + 1]
    tag_mapping[tag] = [older_tag, tag]
  end
  tag_mapping
end
compound_changelog() click to toggle source

Main function to start changelog generation

@return [String] Generated changelog file

# File lib/github_changelog_generator/generator/generator.rb, line 50
def compound_changelog
  @options.load_custom_ruby_files

  Sync do
    fetch_and_filter_tags
    fetch_issues_and_pr

    log = if @options[:unreleased_only]
            generate_entry_between_tags(@filtered_tags[0], nil)
          else
            generate_entries_for_all_tags
          end
    log += File.read(@options[:base]) if File.file?(@options[:base])
    log = remove_old_fixed_string(log)
    log = insert_fixed_string(log)
    @log = log
  end
end
delete_by_time(issues, hash_key = "actual_date", older_tag = nil, newer_tag = nil) click to toggle source

Method filter issues, that belong only specified tag range @param [Array] issues issues to filter @param [Symbol] hash_key key of date value default is :actual_date @param [Hash, Nil] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag @param [Hash, Nil] newer_tag all issue after this tag will be excluded. May be nil for unreleased section @return [Array] filtered issues

# File lib/github_changelog_generator/generator/generator_processor.rb, line 99
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) click to toggle source

Find correct closed dates, if issues was closed by commits

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 26
def detect_actual_closed_dates(issues)
  print "Fetching closed dates for issues...\r" if options[:verbose]

  i = 0
  issues.each do |issue|
    find_closed_date_by_commit(issue)
    i += 1
  end
  puts "Fetching closed dates for issues: #{i}/#{issues.count}" if options[:verbose]
end
due_tag() click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 100
def due_tag
  @due_tag ||= options.fetch(:due_tag, nil)
end
ensure_older_tag(older_tag, newer_tag) click to toggle source
# File lib/github_changelog_generator/generator/generator_processor.rb, line 125
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) click to toggle source

delete all issues with labels from options array @param [Array] issues @return [Array] filtered array

# File lib/github_changelog_generator/generator/generator_processor.rb, line 8
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
exclude_issues_without_labels(issues) click to toggle source

Only include issues without labels if options @param [Array] issues @return [Array] filtered array

# File lib/github_changelog_generator/generator/generator_processor.rb, line 20
def exclude_issues_without_labels(issues)
  return issues if issues.empty?
  return issues if issues.first.key?("pull_request") && options[:add_pr_wo_labels]
  return issues if !issues.first.key?("pull_request") && options[:add_issues_wo_labels]

  issues.reject do |issue|
    issue["labels"].empty?
  end
end
fetch_and_filter_tags() click to toggle source

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

# File lib/github_changelog_generator/generator/generator_tags.rb, line 6
def fetch_and_filter_tags
  since_tag
  due_tag

  all_tags = @fetcher.get_all_tags
  fetch_tags_dates(all_tags) # Creates a Hash @tag_times_hash
  all_sorted_tags = sort_tags_by_date(all_tags)

  @sorted_tags   = filter_included_tags(all_sorted_tags)
  @sorted_tags   = filter_excluded_tags(@sorted_tags)
  @filtered_tags = get_filtered_tags(@sorted_tags)
  @tag_section_mapping = build_tag_section_mapping(@filtered_tags, @filtered_tags)

  @filtered_tags
end
fetch_events_for_issues_and_pr() click to toggle source

Fetch event for issues and pull requests @return [Array] array of fetched issues

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 7
def fetch_events_for_issues_and_pr
  print "Fetching events for issues and PR: 0/#{@issues.count + @pull_requests.count}\r" if options[:verbose]

  # Async fetching events:
  @fetcher.fetch_events_async(@issues + @pull_requests)
end
fetch_tags_dates(tags) click to toggle source

Async fetching of all tags dates

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 15
def fetch_tags_dates(tags)
  print "Fetching tag dates...\r" if options[:verbose]
  i = 0
  tags.each do |tag|
    get_time_of_tag(tag)
    i += 1
  end
  puts "Fetching tags dates: #{i}/#{tags.count}" if options[:verbose]
end
filter_array_by_labels(all_issues) click to toggle source

General filtered function

@param [Array] all_issues PRs or issues @return [Array] filtered issues

# File lib/github_changelog_generator/generator/generator_processor.rb, line 189
def filter_array_by_labels(all_issues)
  filtered_issues = include_issues_by_labels(all_issues)
  filtered_issues = exclude_issues_by_labels(filtered_issues)
  exclude_issues_without_labels(filtered_issues)
end
filter_by_include_labels(issues) click to toggle source

@todo Document this @param [Object] issues

# File lib/github_changelog_generator/generator/generator_processor.rb, line 174
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? || issue["labels"].empty?
    end
  end
end
filter_by_milestone(filtered_issues, tag_name, all_issues) click to toggle source

@return [Array] filtered issues accourding milestone

# File lib/github_changelog_generator/generator/generator_processor.rb, line 31
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_by_tag(issues, newer_tag = nil) click to toggle source

Method filter issues, that belong only specified tag range

@param [Array] issues issues to filter @param [Hash, Nil] newer_tag Tag to find PRs of. May be nil for unreleased section @return [Array] filtered issues

# File lib/github_changelog_generator/generator/generator_processor.rb, line 87
def filter_by_tag(issues, newer_tag = nil)
  issues.select do |issue|
    issue["first_occurring_tag"] == (newer_tag.nil? ? nil : newer_tag["name"])
  end
end
filter_due_tag(all_tags) click to toggle source

@param [Array] all_tags all tags @return [Array] filtered tags according :due_tag option

# File lib/github_changelog_generator/generator/generator_tags.rb, line 141
def filter_due_tag(all_tags)
  filtered_tags = all_tags
  tag           = 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
      raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --due-tag option."
    end
  end
  filtered_tags
end
filter_excluded_tags(all_tags) click to toggle source

@param [Array] all_tags all tags @return [Array] filtered tags according :exclude_tags or :exclude_tags_regex option

# File lib/github_changelog_generator/generator/generator_tags.rb, line 172
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_included_tags(all_tags) click to toggle source

@param [Array] all_tags all tags @return [Array] filtered tags according to :include_tags_regex option

# File lib/github_changelog_generator/generator/generator_tags.rb, line 161
def filter_included_tags(all_tags)
  if options[:include_tags_regex]
    regex = Regexp.new(options[:include_tags_regex])
    all_tags.select { |tag| regex =~ tag["name"] }
  else
    all_tags
  end
end
filter_merged_pull_requests(pull_requests) click to toggle source

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.

# File lib/github_changelog_generator/generator/generator_processor.rb, line 218
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.reject! do |pr|
    pr["merged_at"].nil?
  end

  pull_requests
end
filter_since_tag(all_tags) click to toggle source

@param [Array] all_tags all tags @return [Array] filtered tags according :since_tag option

# File lib/github_changelog_generator/generator/generator_tags.rb, line 121
def filter_since_tag(all_tags)
  filtered_tags = all_tags
  tag = 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
                        all_tags[0..idx]
                      else
                        []
                      end
    else
      raise ChangelogGeneratorError, "Error: can't find tag #{tag}, specified with --since-tag option."
    end
  end
  filtered_tags
end
filter_wo_labels(items) click to toggle source

@param [Array] items Issues & PRs to filter when without labels @return [Array] Issues & PRs without labels or empty array if

add_issues_wo_labels or add_pr_wo_labels are false
# File lib/github_changelog_generator/generator/generator_processor.rb, line 162
def filter_wo_labels(items)
  if items.any? && items.first.key?("pull_request")
    return items if options[:add_pr_wo_labels]
  elsif options[:add_issues_wo_labels]
    return items
  end
  # The default is to filter items without labels
  items.select { |item| item["labels"].map { |l| l["name"] }.any? }
end
find_closed_date_by_commit(issue) click to toggle source

Fill :actual_date parameter of specified issue by closed date of the commit, if it was closed by commit. @param [Hash] issue

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 156
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) click to toggle source

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

@param [Array] all_issues @param [String] tag_name @return [Array] issues with milestone tag_name

# File lib/github_changelog_generator/generator/generator_processor.rb, line 47
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
get_filtered_issues(issues) click to toggle source

Filter issues according labels @return [Array] Filtered issues

# File lib/github_changelog_generator/generator/generator_processor.rb, line 197
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) click to toggle source

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. @return [Array] filtered PR's

# File lib/github_changelog_generator/generator/generator_processor.rb, line 207
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) click to toggle source

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

@return [Array]

# File lib/github_changelog_generator/generator/generator_tags.rb, line 114
def get_filtered_tags(all_tags)
  filtered_tags = filter_since_tag(all_tags)
  filter_due_tag(filtered_tags)
end
get_time_of_tag(tag_name) click to toggle source

Returns date for given GitHub Tag hash

Memoize the date by tag name.

@param [Hash] tag_name

@return [Time] time of specified tag

# File lib/github_changelog_generator/generator/generator_tags.rb, line 63
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) click to toggle source

Include issues with labels, specified in :include_labels @param [Array] issues to filter @return [Array] filtered array of issues

# File lib/github_changelog_generator/generator/generator_processor.rb, line 154
def include_issues_by_labels(issues)
  filtered_issues = filter_by_include_labels(issues)
  filter_wo_labels(filtered_issues)
end
remove_issues_in_milestones(filtered_issues) click to toggle source

@return [Array] array with removed issues, that contain milestones with same name as a tag

# File lib/github_changelog_generator/generator/generator_processor.rb, line 67
def remove_issues_in_milestones(filtered_issues)
  filtered_issues.select! do |issue|
    # leave issues without milestones
    if issue["milestone"].nil?
      true
    # remove issues of open milestones if option is set
    elsif issue["milestone"]["state"] == "open"
      @options[:issues_of_open_milestones]
    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) click to toggle source

Set closed date from this issue

@param [Hash] event @param [Hash] issue

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 175
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["commit_id"])
      issue["actual_date"] = commit["commit"]["author"]["date"]

      # issue['actual_date'] = commit['author']['date']
    rescue StandardError
      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
since_tag() click to toggle source

@return [Object] try to find newest tag using #Reader and :base option if specified otherwise returns nil

# File lib/github_changelog_generator/generator/generator_tags.rb, line 96
def since_tag
  @since_tag ||= options.fetch(:since_tag) { version_of_first_item }
end
sort_tags_by_date(tags) click to toggle source

Sort all tags by date, newest to oldest

# File lib/github_changelog_generator/generator/generator_tags.rb, line 49
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, time) click to toggle source
# File lib/github_changelog_generator/generator/generator_processor.rb, line 143
def tag_newer_old_tag?(older_tag_time, time)
  if older_tag_time.nil?
    true
  else
    time > older_tag_time
  end
end
tag_older_new_tag?(newer_tag_time, time) click to toggle source
# File lib/github_changelog_generator/generator/generator_processor.rb, line 135
def tag_older_new_tag?(newer_tag_time, time)
  if newer_tag_time.nil?
    true
  else
    time <= newer_tag_time
  end
end
version_of_first_item() click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 104
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

Private Instance Methods

apply_exclude_tags(all_tags) click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 184
def apply_exclude_tags(all_tags)
  if options[:exclude_tags].is_a?(Regexp)
    filter_tags_with_regex(all_tags, options[:exclude_tags], "--exclude-tags")
  else
    filter_exact_tags(all_tags)
  end
end
apply_exclude_tags_regex(all_tags) click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 192
def apply_exclude_tags_regex(all_tags)
  regex = Regexp.new(options[:exclude_tags_regex])
  filter_tags_with_regex(all_tags, regex, "--exclude-tags-regex")
end
fetch_issues_and_pr() click to toggle source

Fetches @pull_requests and @issues and filters them based on options.

@return [Nil] No return.

# File lib/github_changelog_generator/generator/generator.rb, line 143
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)
  add_first_occurring_tag_to_prs(@sorted_tags, @pull_requests)
  nil
end
filter_exact_tags(all_tags) click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 202
def filter_exact_tags(all_tags)
  options[:exclude_tags].each do |tag|
    warn_if_tag_not_found(all_tags, tag)
  end
  all_tags.reject { |tag| options[:exclude_tags].include?(tag["name"]) }
end
filter_issues_for_tags(newer_tag, older_tag) click to toggle source

Filters issues and pull requests based on, respectively, `actual_date` and `merged_at` timestamp fields. `actual_date` is the detected form of `closed_at` based on merge event SHA commit times.

@return [Array] filtered issues and pull requests

# File lib/github_changelog_generator/generator/generator.rb, line 101
def filter_issues_for_tags(newer_tag, older_tag)
  filtered_pull_requests = filter_by_tag(@pull_requests, 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_tags_with_regex(all_tags, regex, regex_option_name) click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 197
def filter_tags_with_regex(all_tags, regex, regex_option_name)
  warn_if_nonmatching_regex(all_tags, regex, regex_option_name)
  all_tags.reject { |tag| regex =~ tag["name"] }
end
generate_entries_for_all_tags() click to toggle source

The full cycle of generation for whole project @return [String] All entries in the changelog

# File lib/github_changelog_generator/generator/generator.rb, line 117
def generate_entries_for_all_tags
  puts "Generating entry..." if options[:verbose]

  entries = generate_unreleased_entry

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

  entries
end
generate_entry_between_tags(older_tag, newer_tag) click to toggle source

Generate log only between 2 specified tags @param [String] older_tag all issues before this tag date will be excluded. May be nil, if it's first tag @param [String] newer_tag all issue after this tag will be excluded. May be nil for unreleased section

# File lib/github_changelog_generator/generator/generator.rb, line 74
def generate_entry_between_tags(older_tag, newer_tag)
  filtered_issues, filtered_pull_requests = filter_issues_for_tags(newer_tag, older_tag)

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

  newer_tag_link, newer_tag_name, newer_tag_time = detect_link_tag_time(newer_tag)

  # If the older tag is nil, go back in time from the latest tag and find
  # the SHA for the first commit.
  older_tag_name =
    if older_tag.nil?
      @fetcher.oldest_commit["sha"]
    else
      older_tag["name"]
    end

  Entry.new(options).generate_entry_for_tag(filtered_pull_requests, filtered_issues, newer_tag_name, newer_tag_link, newer_tag_time, older_tag_name)
end
generate_unreleased_entry() click to toggle source
# File lib/github_changelog_generator/generator/generator.rb, line 130
def generate_unreleased_entry
  entry = ""
  if options[:unreleased]
    start_tag        = @filtered_tags[0] || @sorted_tags.last
    unreleased_entry = generate_entry_between_tags(start_tag, nil)
    entry           += unreleased_entry if unreleased_entry
  end
  entry
end
insert_fixed_string(log) click to toggle source

Add template messages to given string. Previously added messages of the same wording are removed. @param log [String]

# File lib/github_changelog_generator/generator/generator.rb, line 168
def insert_fixed_string(log)
  ins = ""
  ins += @options[:frontmatter] if @options[:frontmatter]
  ins += "#{@options[:header]}\n\n"
  log.insert(0, ins)
  log += "\n\n#{CREDIT_LINE}"
  log
end
remove_old_fixed_string(log) click to toggle source

Remove the previously assigned fixed message. @param log [String] Old lines are fixed

# File lib/github_changelog_generator/generator/generator.rb, line 158
def remove_old_fixed_string(log)
  log.gsub!(/#{Regexp.escape(@options[:frontmatter])}/, "") if @options[:frontmatter]
  log.gsub!(/#{Regexp.escape(@options[:header])}\n{,2}/, "")
  log.gsub!(/\n{,2}#{Regexp.escape(CREDIT_LINE)}/, "") # Remove old credit lines
  log
end
sha_in_release_branch?(sha) click to toggle source

Detect if a sha occurs in the –release-branch. Uses the github repo default branch if not specified.

@param [String] sha SHA to check. @return [Boolean] True if SHA is in the branch git history.

# File lib/github_changelog_generator/generator/generator_fetcher.rb, line 198
def sha_in_release_branch?(sha)
  branch = @options[:release_branch] || @fetcher.default_branch
  @fetcher.commits_in_branch(branch).include?(sha)
end
warn_if_nonmatching_regex(all_tags, regex, regex_option_name) click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 209
def warn_if_nonmatching_regex(all_tags, regex, regex_option_name)
  unless all_tags.map { |t| t["name"] }.any? { |t| regex =~ t }
    Helper.log.warn "Warning: unable to reject any tag, using regex "\
                    "#{regex.inspect} in #{regex_option_name} option."
  end
end
warn_if_tag_not_found(all_tags, tag) click to toggle source
# File lib/github_changelog_generator/generator/generator_tags.rb, line 216
def warn_if_tag_not_found(all_tags, tag)
  Helper.log.warn("Warning: can't find tag #{tag}, specified with --exclude-tags option.") unless all_tags.map { |t| t["name"] }.include?(tag)
end