From c05ec197abb7b2674ff687ca707370157fe0f186 Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Sat, 24 Jun 2017 10:59:23 +0200 Subject: [PATCH 1/5] add breaking-changes section to changelog --- .../generator/generator.rb | 20 ++++++++++++++++--- lib/github_changelog_generator/options.rb | 2 ++ lib/github_changelog_generator/parser.rb | 8 ++++++++ lib/github_changelog_generator/parser_file.rb | 3 ++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator.rb b/lib/github_changelog_generator/generator/generator.rb index b349f63ec..98173eaf3 100644 --- a/lib/github_changelog_generator/generator/generator.rb +++ b/lib/github_changelog_generator/generator/generator.rb @@ -96,24 +96,27 @@ def create_log_for_tag(pull_requests, issues, newer_tag, older_tag = nil) # @return [String] generated log for issues def issues_to_log(issues, pull_requests) log = "" - bugs_a, enhancement_a, issues_a = parse_by_sections(issues, pull_requests) + bugs_a, enhancement_a, breaking_a, issues_a = parse_by_sections(issues, pull_requests) + log += generate_sub_section(breaking_a, options[:breaking_prefix]) 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 + # rubocop:disable Metrics/CyclomaticComplexity # This method sort issues by types # (bugs, features, or just closed issues) by labels # # @param [Array] issues # @param [Array] pull_requests - # @return [Array] tuple of filtered arrays: (Bugs, Enhancements Issues) + # @return [Array] tuple of filtered arrays: (Bugs, Enhancements, Breaking stuff, Issues) def parse_by_sections(issues, pull_requests) issues_a = [] enhancement_a = [] bugs_a = [] + breaking_a = [] issues.each do |dict| added = false @@ -128,9 +131,15 @@ def parse_by_sections(issues, pull_requests) added = true next end + if options[:breaking_labels].include?(label["name"]) + breaking_a.push(dict) + added = true + next + end end issues_a.push(dict) unless added end + # rubocop:enable Metrics/CyclomaticComplexity added_pull_requests = [] pull_requests.each do |pr| @@ -145,11 +154,16 @@ def parse_by_sections(issues, pull_requests) added_pull_requests.push(pr) next end + if options[:breaking_labels].include?(label["name"]) + breaking_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] + [bugs_a, enhancement_a, breaking_a, issues_a] end end end diff --git a/lib/github_changelog_generator/options.rb b/lib/github_changelog_generator/options.rb index 0370df5b0..c0bb5853a 100644 --- a/lib/github_changelog_generator/options.rb +++ b/lib/github_changelog_generator/options.rb @@ -20,6 +20,8 @@ class Options < SimpleDelegator due_tag enhancement_labels enhancement_prefix + breaking_labels + breaking_prefix exclude_labels exclude_tags exclude_tags_regex diff --git a/lib/github_changelog_generator/parser.rb b/lib/github_changelog_generator/parser.rb index dd0c1ba81..d02eab8ae 100755 --- a/lib/github_changelog_generator/parser.rb +++ b/lib/github_changelog_generator/parser.rb @@ -72,6 +72,9 @@ def self.setup_parser(options) opts.on("--enhancement-label [LABEL]", "Setup custom label for enhancements section. Default is \"**Implemented enhancements:**\"") do |v| options[:enhancement_prefix] = v end + opts.on("--breaking-label [LABEL]", "Setup custom label for the breaking changes section. Default is \"**Breaking changes:**\"") do |v| + options[:breaking_prefix] = v + end opts.on("--issues-label [LABEL]", "Setup custom label for closed-issues section. Default is \"**Closed issues:**\"") do |v| options[:issue_prefix] = v end @@ -129,6 +132,9 @@ def self.setup_parser(options) opts.on("--enhancement-labels x,y,z", Array, 'Issues with the specified labels will be always added to "Implemented enhancements" section. Default is \'enhancement,Enhancement\'') do |list| options[:enhancement_labels] = list end + opts.on("--breaking-labels x,y,z", Array, 'Issues with these labels will be added to a new section, called "Breaking Changes". Default is \'backwards-incompatible\'') do |list| + options[:breaking_labels] = list + end opts.on("--issue-line-labels x,y,z", Array, 'The specified labels will be shown in brackets next to each matching issue. Use "ALL" to show all labels. Default is [].') do |list| options[:issue_line_labels] = list end @@ -210,6 +216,7 @@ def self.default_options enhancement_labels: ["enhancement", "Enhancement", "Type: Enhancement"], bug_labels: ["bug", "Bug", "Type: Bug"], exclude_labels: ["duplicate", "question", "invalid", "wontfix", "Duplicate", "Question", "Invalid", "Wontfix", "Meta: Exclude From Changelog"], + breaking_labels: %w[backwards-incompatible breaking], issue_line_labels: [], max_issues: nil, simple_list: false, @@ -220,6 +227,7 @@ def self.default_options issue_prefix: "**Closed issues:**", bug_prefix: "**Fixed bugs:**", enhancement_prefix: "**Implemented enhancements:**", + breaking_prefix: "**Breaking changes:**", git_remote: "origin", http_cache: true ) diff --git a/lib/github_changelog_generator/parser_file.rb b/lib/github_changelog_generator/parser_file.rb index e2a80d716..182ece6ff 100644 --- a/lib/github_changelog_generator/parser_file.rb +++ b/lib/github_changelog_generator/parser_file.rb @@ -67,7 +67,7 @@ def extract_pair(line) end KNOWN_ARRAY_KEYS = %i[exclude_labels include_labels bug_labels - enhancement_labels issue_line_labels between_tags exclude_tags] + enhancement_labels breaking_labels issue_line_labels between_tags exclude_tags] KNOWN_INTEGER_KEYS = [:max_issues] def convert_value(value, option_name) @@ -91,6 +91,7 @@ def convert_value(value, option_name) header_label: :header, front_matter: :frontmatter, pr_label: :merge_prefix, + breaking_label: :breaking_prefix, issues_wo_labels: :add_issues_wo_labels, pr_wo_labels: :add_pr_wo_labels, pull_requests: :pulls, From b4e7cc74ca350b1b74ffee07aaf10ef281a72c82 Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Sat, 30 Sep 2017 22:09:36 +0200 Subject: [PATCH 2/5] replace legacy push with shovel operator --- .../generator/generator.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator.rb b/lib/github_changelog_generator/generator/generator.rb index 98173eaf3..e8c913348 100644 --- a/lib/github_changelog_generator/generator/generator.rb +++ b/lib/github_changelog_generator/generator/generator.rb @@ -122,22 +122,22 @@ def parse_by_sections(issues, pull_requests) added = false dict["labels"].each do |label| if options[:bug_labels].include?(label["name"]) - bugs_a.push(dict) + bugs_a << dict added = true next end if options[:enhancement_labels].include?(label["name"]) - enhancement_a.push(dict) + enhancement_a << dict added = true next end if options[:breaking_labels].include?(label["name"]) - breaking_a.push(dict) + breaking_a << dict added = true next end end - issues_a.push(dict) unless added + issues_a << dict unless added end # rubocop:enable Metrics/CyclomaticComplexity @@ -145,18 +145,18 @@ def parse_by_sections(issues, 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) + bugs_a << pr + added_pull_requests << pr next end if options[:enhancement_labels].include?(label["name"]) - enhancement_a.push(pr) - added_pull_requests.push(pr) + enhancement_a << pr + added_pull_requests << pr next end if options[:breaking_labels].include?(label["name"]) - breaking_a.push(pr) - added_pull_requests.push(pr) + breaking_a << pr + added_pull_requests << pr next end end From eb9e83a8b58229d3f73b5d36882656c0ce39be36 Mon Sep 17 00:00:00 2001 From: Ewoud Kohl van Wijngaarden Date: Fri, 6 Oct 2017 16:12:45 +0200 Subject: [PATCH 3/5] Clean up parse_by_sections --- .../generator/generator.rb | 71 ++++++++++--------- .../generator/generator_generation_spec.rb | 56 +++++++++++++++ 2 files changed, 92 insertions(+), 35 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator.rb b/lib/github_changelog_generator/generator/generator.rb index e8c913348..8243eb022 100644 --- a/lib/github_changelog_generator/generator/generator.rb +++ b/lib/github_changelog_generator/generator/generator.rb @@ -95,75 +95,76 @@ def create_log_for_tag(pull_requests, issues, newer_tag, older_tag = nil) # @param [Array] pull_requests # @return [String] generated log for issues def issues_to_log(issues, pull_requests) - log = "" - bugs_a, enhancement_a, breaking_a, issues_a = parse_by_sections(issues, pull_requests) + sections = parse_by_sections(issues, pull_requests) - log += generate_sub_section(breaking_a, options[:breaking_prefix]) - 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 = "" + log += generate_sub_section(sections[:breaking], options[:breaking_prefix]) + log += generate_sub_section(sections[:enhancement], options[:enhancement_prefix]) + log += generate_sub_section(sections[:bugs], options[:bug_prefix]) + log += generate_sub_section(sections[:issues], options[:issue_prefix]) log end - # rubocop:disable Metrics/CyclomaticComplexity # This method sort issues by types # (bugs, features, or just closed issues) by labels # # @param [Array] issues # @param [Array] pull_requests - # @return [Array] tuple of filtered arrays: (Bugs, Enhancements, Breaking stuff, Issues) + # @return [Hash] Mapping of filtered arrays: (Bugs, Enhancements, Breaking stuff, Issues) def parse_by_sections(issues, pull_requests) - issues_a = [] - enhancement_a = [] - bugs_a = [] - breaking_a = [] + sections = { + :issues => [], + :enhancements => [], + :bugs => [], + :breaking => [], + } issues.each do |dict| added = false + dict["labels"].each do |label| if options[:bug_labels].include?(label["name"]) - bugs_a << dict + sections[:bugs] << dict added = true - next - end - if options[:enhancement_labels].include?(label["name"]) - enhancement_a << dict + elsif options[:enhancement_labels].include?(label["name"]) + sections[:enhancements] << dict added = true - next - end - if options[:breaking_labels].include?(label["name"]) - breaking_a << dict + elsif options[:breaking_labels].include?(label["name"]) + sections[:breaking] << dict added = true - next end + + break if added end - issues_a << dict unless added + + sections[:issues] << dict unless added end - # rubocop:enable Metrics/CyclomaticComplexity added_pull_requests = [] pull_requests.each do |pr| + added = false + pr["labels"].each do |label| if options[:bug_labels].include?(label["name"]) - bugs_a << pr + sections[:bugs] << pr added_pull_requests << pr - next - end - if options[:enhancement_labels].include?(label["name"]) - enhancement_a << pr + added = true + elsif options[:enhancement_labels].include?(label["name"]) + sections[:enhancements] << pr added_pull_requests << pr - next - end - if options[:breaking_labels].include?(label["name"]) - breaking_a << pr + added = true + elsif options[:breaking_labels].include?(label["name"]) + sections[:breaking] << pr added_pull_requests << pr - next + added = true end + + break if added end end added_pull_requests.each { |p| pull_requests.delete(p) } - [bugs_a, enhancement_a, breaking_a, issues_a] + sections end end end diff --git a/spec/unit/generator/generator_generation_spec.rb b/spec/unit/generator/generator_generation_spec.rb index c849eda8d..eac38a41b 100644 --- a/spec/unit/generator/generator_generation_spec.rb +++ b/spec/unit/generator/generator_generation_spec.rb @@ -13,5 +13,61 @@ module GitHubChangelogGenerator end.not_to raise_error end end + + describe "#parse_by_sections" do + def label(name) + {"name" => name} + end + + def issue(title, labels) + {"title" => "issue #{title}", "labels" => labels.map { |l| label(l) }} + end + + def pr(title, labels) + {"title" => "pr #{title}", "labels" => labels.map { |l| label(l) }} + end + + def get_titles(issues) + issues.map { |issue| issue["title"] } + end + + let(:options) do + { + :bug_labels => ["bug"], + :enhancement_labels => ["enhancement"], + :breaking_labels => ["breaking"], + } + end + + let(:issues) do + [ + issue("no labels", []), + issue("enhancement", ["enhancement"]), + issue("bug", ["bug"]), + issue("breaking", ["breaking"]), + issue("all the labels", ["enhancement", "bug", "breaking"]), + ] + end + + let(:pull_requests) do + [ + pr("no labels", []), + pr("enhancement", ["enhancement"]), + pr("bug", ["bug"]), + pr("breaking", ["breaking"]), + pr("all the labels", ["enhancement", "bug", "breaking"]), + ] + end + + it "works" do + sections = described_class.new(options).parse_by_sections(issues, pull_requests) + + expect(get_titles(sections[:issues])).to eq(["issue no labels"]) + expect(get_titles(sections[:enhancements])).to eq(["issue enhancement", "issue all the labels", "pr enhancement", "pr all the labels"]) + expect(get_titles(sections[:bugs])).to eq(["issue bug", "pr bug"]) + expect(get_titles(sections[:breaking])).to eq(["issue breaking", "pr breaking"]) + expect(get_titles(pull_requests)).to eq(["pr no labels"]) + end + end end end From 8a7cdaaa5ed2ee51db043d0936271a98335038e1 Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Tue, 10 Oct 2017 19:23:42 +0200 Subject: [PATCH 4/5] rubocop autofixes --- .../generator/generator.rb | 8 ++++---- spec/unit/generator/generator_generation_spec.rb | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/github_changelog_generator/generator/generator.rb b/lib/github_changelog_generator/generator/generator.rb index 8243eb022..84d3bdfd1 100644 --- a/lib/github_changelog_generator/generator/generator.rb +++ b/lib/github_changelog_generator/generator/generator.rb @@ -113,10 +113,10 @@ def issues_to_log(issues, pull_requests) # @return [Hash] Mapping of filtered arrays: (Bugs, Enhancements, Breaking stuff, Issues) def parse_by_sections(issues, pull_requests) sections = { - :issues => [], - :enhancements => [], - :bugs => [], - :breaking => [], + issues: [], + enhancements: [], + bugs: [], + breaking: [] } issues.each do |dict| diff --git a/spec/unit/generator/generator_generation_spec.rb b/spec/unit/generator/generator_generation_spec.rb index eac38a41b..5a1b089cd 100644 --- a/spec/unit/generator/generator_generation_spec.rb +++ b/spec/unit/generator/generator_generation_spec.rb @@ -16,15 +16,15 @@ module GitHubChangelogGenerator describe "#parse_by_sections" do def label(name) - {"name" => name} + { "name" => name } end def issue(title, labels) - {"title" => "issue #{title}", "labels" => labels.map { |l| label(l) }} + { "title" => "issue #{title}", "labels" => labels.map { |l| label(l) } } end def pr(title, labels) - {"title" => "pr #{title}", "labels" => labels.map { |l| label(l) }} + { "title" => "pr #{title}", "labels" => labels.map { |l| label(l) } } end def get_titles(issues) @@ -33,9 +33,9 @@ def get_titles(issues) let(:options) do { - :bug_labels => ["bug"], - :enhancement_labels => ["enhancement"], - :breaking_labels => ["breaking"], + bug_labels: ["bug"], + enhancement_labels: ["enhancement"], + breaking_labels: ["breaking"] } end @@ -45,7 +45,7 @@ def get_titles(issues) issue("enhancement", ["enhancement"]), issue("bug", ["bug"]), issue("breaking", ["breaking"]), - issue("all the labels", ["enhancement", "bug", "breaking"]), + issue("all the labels", %w[enhancement bug breaking]) ] end @@ -55,7 +55,7 @@ def get_titles(issues) pr("enhancement", ["enhancement"]), pr("bug", ["bug"]), pr("breaking", ["breaking"]), - pr("all the labels", ["enhancement", "bug", "breaking"]), + pr("all the labels", %w[enhancement bug breaking]) ] end From 7fc20cd7b3f4b38f2d6405b09d7dc45b60ae7fed Mon Sep 17 00:00:00 2001 From: Tim Meusel Date: Tue, 10 Oct 2017 19:46:53 +0200 Subject: [PATCH 5/5] reduce complexity in parse_by_sections() --- lib/github_changelog_generator/generator/generator.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/github_changelog_generator/generator/generator.rb b/lib/github_changelog_generator/generator/generator.rb index 84d3bdfd1..ee30e426d 100644 --- a/lib/github_changelog_generator/generator/generator.rb +++ b/lib/github_changelog_generator/generator/generator.rb @@ -140,6 +140,15 @@ def parse_by_sections(issues, pull_requests) sections[:issues] << dict unless added end + sort_pull_requests(pull_requests, sections) + end + + # This method iterates through PRs and sorts them into sections + # + # @param [Array] pull_requests + # @param [Hash] sections + # @return [Hash] sections + def sort_pull_requests(pull_requests, sections) added_pull_requests = [] pull_requests.each do |pr| added = false @@ -163,7 +172,6 @@ def parse_by_sections(issues, pull_requests) end end added_pull_requests.each { |p| pull_requests.delete(p) } - sections end end