# frozen_string_literal: true module GitHubChangelogGenerator class Generator # delete all issues with labels from options[:exclude_labels] array # @param [Array] issues # @return [Array] filtered array 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 # Only include issues without labels if options[:add_issues_wo_labels] # @param [Array] issues # @return [Array] filtered array 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 # @return [Array] filtered issues accourding milestone 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 # 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 def find_issues_to_add(all_issues, tag_name) all_issues.select do |issue| if (milestone = issue["milestone"]).nil? false # check, that this milestone in tag list: elsif (tag = find_tag_for_milestone(milestone)).nil? false else tag["name"] == tag_name end end end # @return [Array] array with removed issues, that contain milestones with same name as a tag def remove_issues_in_milestones(filtered_issues) filtered_issues.select! do |issue| # leave issues without milestones if (milestone = issue["milestone"]).nil? true # remove issues of open milestones if option is set elsif milestone["state"] == "open" @options[:issues_of_open_milestones] else # check, that this milestone in tag list: find_tag_for_milestone(milestone).nil? end end end def find_tag_for_milestone(milestone) @filtered_tags.find { |tag| tag["name"] == milestone["title"] } end # 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 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 # 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 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 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 def tag_older_new_tag?(newer_tag_time, time) if newer_tag_time.nil? true else time <= newer_tag_time end end def tag_newer_old_tag?(older_tag_time, time) if older_tag_time.nil? true else time > older_tag_time end end # Include issues with labels, specified in :include_labels # @param [Array] issues to filter # @return [Array] filtered array of issues def include_issues_by_labels(issues) filtered_issues = filter_by_include_labels(issues) filter_wo_labels(filtered_issues) end # @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 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 # @todo Document this # @param [Object] issues 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 # General filtered function # # @param [Array] all_issues PRs or issues # @return [Array] filtered issues 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 issues according labels # @return [Array] Filtered issues def get_filtered_issues(issues) issues = filter_array_by_labels(issues) puts "Filtered issues: #{issues.count}" if options[:verbose] issues end # This method fetches missing params for PR and filter them by specified options # It include add all PR's with labels from options[:include_labels] array # And exclude all from :exclude_labels array. # @return [Array] filtered PR's 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 # 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. 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 end end