|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +require "optparse" |
| 4 | +require "json" |
| 5 | + |
| 6 | +# @return [String] |
| 7 | +def latest_tag |
| 8 | + `git tag`.each_line.map(&:strip).max_by { |tag| Gem::Version.create(tag.delete_prefix("v")) } |
| 9 | +end |
| 10 | + |
| 11 | +# @param before [String] |
| 12 | +# @param after [String] |
| 13 | +# @return [Array<Integer>] |
| 14 | +def search_pr_numbers(before:, after:) |
| 15 | + commits = `git rev-list --merges --right-only #{before}...#{after}`.each_line.map(&:strip) |
| 16 | + commits.map do |commit| |
| 17 | + commit_message = `git show -q #{commit}` |
| 18 | + commit_message =~ /Merge pull request #([0-9]+)/ |
| 19 | + Regexp.last_match(1).to_i |
| 20 | + end |
| 21 | +end |
| 22 | + |
| 23 | +before = nil |
| 24 | +after = nil |
| 25 | + |
| 26 | +opt = OptionParser.new |
| 27 | +opt.on("--before=BEFORE", "Before tag or sha1 (default: latest tag)") { |v| before = v } |
| 28 | +opt.on("--after=AFTER", "After tag or sha1 (default: HEAD)") { |v| after = v } |
| 29 | + |
| 30 | +opt.parse!(ARGV) |
| 31 | + |
| 32 | +before ||= latest_tag |
| 33 | +after ||= "HEAD" |
| 34 | + |
| 35 | +pr_numbers = search_pr_numbers(before:, after:) |
| 36 | +all_prs = pr_numbers.map do |pr_number| |
| 37 | + pr = JSON.parse(`gh pr view --json number,title,author,labels,url #{pr_number}`) |
| 38 | + pr["label_names"] = pr["labels"].map { |label| label["name"] } |
| 39 | + pr |
| 40 | +end |
| 41 | + |
| 42 | +# @param prs [Array<Hash>] |
| 43 | +# |
| 44 | +# @return [String] |
| 45 | +def generate_category_changelog(prs) |
| 46 | + lines = [] |
| 47 | + found_pr_numbers = [] |
| 48 | + prs = prs.dup |
| 49 | + |
| 50 | + ["breaking change", "bug", "enhancement"].each do |label| |
| 51 | + label_prs = prs.find_all { |pr| pr["label_names"].include?(label) } |
| 52 | + |
| 53 | + next if label_prs.empty? |
| 54 | + |
| 55 | + case label |
| 56 | + when "breaking change" |
| 57 | + lines << "### :bomb: Breaking changes" |
| 58 | + when "bug" |
| 59 | + lines << "### Bugfixes" |
| 60 | + when "enhancement" |
| 61 | + lines << "### New Features" |
| 62 | + end |
| 63 | + |
| 64 | + label_prs.each do |pr| |
| 65 | + lines < generate_changelog_line(pr) |
| 66 | + found_pr_numbers << pr["number"] |
| 67 | + end |
| 68 | + |
| 69 | + found_pr_numbers.push(*label_prs.map { |pr| pr["number"] }) |
| 70 | + |
| 71 | + prs.reject! { |pr| found_pr_numbers.include?(pr["number"]) } |
| 72 | + |
| 73 | + lines << "" |
| 74 | + end |
| 75 | + |
| 76 | + other_prs = prs.reject do |pr| |
| 77 | + found_pr_numbers.include?(pr["number"]) || pr["label_names"].include?("chore") |
| 78 | + end |
| 79 | + |
| 80 | + lines << "### Other changes" |
| 81 | + other_prs.each do |pr| |
| 82 | + lines << generate_changelog_line(pr) |
| 83 | + end |
| 84 | + found_pr_numbers.push(*other_prs.map { |pr| pr["number"] }) |
| 85 | + lines << "" |
| 86 | + |
| 87 | + return "* No changes\n\n" if found_pr_numbers.empty? |
| 88 | + |
| 89 | + "#{lines.join("\n")}\n" |
| 90 | +end |
| 91 | + |
| 92 | +# @param pr [Hash] |
| 93 | +# |
| 94 | +# @return [String] |
| 95 | +def generate_changelog_line(pr) |
| 96 | + author = pr["author"]["login"].delete_prefix("app/") |
| 97 | + |
| 98 | + "* #{pr["title"]} by @#{author} in #{pr["url"]}" |
| 99 | +end |
| 100 | + |
| 101 | +changelog_body = +"" |
| 102 | + |
| 103 | +changelog_body << "## Go\n" |
| 104 | +go_prs = all_prs.find_all { |pr| pr["label_names"].include?("go") } |
| 105 | +changelog_body << generate_category_changelog(go_prs) |
| 106 | + |
| 107 | +changelog_body << "## Ruby\n" |
| 108 | +ruby_prs = all_prs.find_all { |pr| pr["label_names"].include?("ruby") } |
| 109 | +changelog_body << generate_category_changelog(ruby_prs) |
| 110 | + |
| 111 | +changelog_body << "## ruby_h_to_go\n" |
| 112 | +ruby_h_to_go_prs = all_prs.find_all { |pr| pr["label_names"].include?("ruby_h_to_go") } |
| 113 | +changelog_body << generate_category_changelog(ruby_h_to_go_prs) |
| 114 | + |
| 115 | +changelog_body << "## Other\n" |
| 116 | +other_prs = all_prs.reject { |pr| pr["label_names"].any? { |label| %w[go ruby ruby_h_to_go chore].include?(label) } } |
| 117 | +changelog_body << generate_category_changelog(other_prs) |
| 118 | + |
| 119 | +changelog_body << "**Full Changelog**: https://github.com/ruby-go-gem/go-gem-wrapper/compare/#{before}...#{after}" |
| 120 | + |
| 121 | +puts changelog_body |
0 commit comments