|
| 1 | +#!/usr/bin/env ruby |
| 2 | +# Licensed to Elasticsearch B.V. under one or more contributor |
| 3 | +# license agreements. See the NOTICE file distributed with |
| 4 | +# this work for additional information regarding copyright |
| 5 | +# ownership. Elasticsearch B.V. licenses this file to you under |
| 6 | +# the Apache License, Version 2.0 (the "License"); you may |
| 7 | +# not use this file except in compliance with the License. |
| 8 | +# You may obtain a copy of the License at |
| 9 | +# |
| 10 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +# |
| 12 | +# Unless required by applicable law or agreed to in writing, |
| 13 | +# software distributed under the License is distributed on an |
| 14 | +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | +# KIND, either express or implied. See the License for the |
| 16 | +# specific language governing permissions and limitations |
| 17 | +# under the License. |
| 18 | + |
| 19 | +# Example: |
| 20 | +# ruby generate_release_notes.rb 6.4 6.4.1 |
| 21 | +# |
| 22 | +# This: |
| 23 | +# * compares the lock file of two commits |
| 24 | +# * for each plugin version bumped show CHANGELOG.md of the bumped version |
| 25 | +require 'tempfile' |
| 26 | +require 'yaml' |
| 27 | +require 'json' |
| 28 | +require 'net/http' |
| 29 | + |
| 30 | +RELEASE_NOTES_PATH = "docs/release-notes/index.md" |
| 31 | +release_branch = ARGV[0] |
| 32 | +previous_release_tag = ARGV[1] |
| 33 | +user = ARGV[2] |
| 34 | +token = ARGV[3] |
| 35 | +report = [] |
| 36 | + |
| 37 | +`git checkout #{release_branch}` |
| 38 | + |
| 39 | +current_release = YAML.load(IO.read("versions.yml"))["logstash"] |
| 40 | + |
| 41 | +release_notes = IO.read(RELEASE_NOTES_PATH).split("\n") |
| 42 | + |
| 43 | +coming_tag_index = release_notes.find_index {|line| line.match(/^## #{current_release} \[logstash-#{current_release}-release-notes\]$/) } |
| 44 | +coming_tag_index += 1 if coming_tag_index |
| 45 | +release_notes_entry_index = coming_tag_index || release_notes.find_index {|line| line.match(/^## .*\[logstash-.*-release-notes\]$/) } |
| 46 | + |
| 47 | +unless coming_tag_index |
| 48 | + report << "## #{current_release} [logstash-#{current_release}-release-notes]\n\n" |
| 49 | + report << "### Features and enhancements [logstash-#{current_release}-features-enhancements]\n" |
| 50 | +end |
| 51 | + |
| 52 | +plugin_changes = {} |
| 53 | + |
| 54 | +report << "---------- GENERATED CONTENT STARTS HERE ------------" |
| 55 | +report << "=== Logstash Pull Requests with label v#{current_release}\n" |
| 56 | + |
| 57 | +uri = URI.parse("https://api.github.com/search/issues?q=repo:elastic/logstash+is:pr+is:closed+label:v#{current_release}&sort=created&order=asc") |
| 58 | +pull_requests = JSON.parse(Net::HTTP.get(uri)) |
| 59 | +pull_requests['items'].each do |prs| |
| 60 | + report << "* #{prs['title']} #{prs['html_url']}[##{prs['number']}]" |
| 61 | +end |
| 62 | +report << "" |
| 63 | + |
| 64 | +report << "=== Logstash Commits between #{release_branch} and #{previous_release_tag}\n" |
| 65 | +report << "Computed with \"git log --pretty=format:'%h -%d %s (%cr) <%an>' --abbrev-commit --date=relative v#{previous_release_tag}..#{release_branch}\"" |
| 66 | +report << "" |
| 67 | +logstash_prs = `git log --pretty=format:'%h -%d %s (%cr) <%an>' --abbrev-commit --date=relative v#{previous_release_tag}..#{release_branch}` |
| 68 | +report << logstash_prs |
| 69 | +report << "\n=== Logstash Plugin Release Changelogs ===" |
| 70 | +report << "Computed from \"git diff v#{previous_release_tag}..#{release_branch} *.release\"" |
| 71 | +result = `git diff v#{previous_release_tag}..#{release_branch} *.release`.split("\n") |
| 72 | + |
| 73 | +result.each do |line| |
| 74 | + # example "+ logstash-input-syslog (3.4.1)" |
| 75 | + if match = line.match(/\+\s+(?<plugin>logstash-.+?-.+?)\s+\((?<version>\d+\.\d+.\d+).*?\)/) |
| 76 | + plugin_changes[match[:plugin]] ||= [] |
| 77 | + plugin_changes[match[:plugin]] << match[:version] |
| 78 | + elsif match = line.match(/\-\s+(?<plugin>logstash-.+?-.+?)\s+\((?<version>\d+\.\d+.\d+).*?\)/) |
| 79 | + plugin_changes[match[:plugin]] ||= [] |
| 80 | + plugin_changes[match[:plugin]].unshift(match[:version]) |
| 81 | + else |
| 82 | + # .. |
| 83 | + end |
| 84 | +end |
| 85 | +report << "Changed plugin versions:" |
| 86 | +plugin_changes.each {|p, v| report << "#{p}: #{v.first} -> #{v.last}" } |
| 87 | +report << "---------- GENERATED CONTENT ENDS HERE ------------\n" |
| 88 | + |
| 89 | +report << "### Plugins [logstash-plugin-#{current_release}-changes]\n" |
| 90 | + |
| 91 | +plugin_changes.each do |plugin, versions| |
| 92 | + _, type, name = plugin.split("-") |
| 93 | + header = "**#{name.capitalize} #{type.capitalize} - #{versions.last}**" |
| 94 | + start_changelog_file = Tempfile.new(plugin + 'start') |
| 95 | + end_changelog_file = Tempfile.new(plugin + 'end') |
| 96 | + changelog = `curl https://raw.githubusercontent.com/logstash-plugins/#{plugin}/v#{versions.last}/CHANGELOG.md`.split("\n") |
| 97 | + report << "#{header}\n" |
| 98 | + changelog.each do |line| |
| 99 | + break if line.match(/^## #{versions.first}/) |
| 100 | + next if line.match(/^##/) |
| 101 | + line.gsub!(/^\+/, "") |
| 102 | + line.gsub!(/ #(?<number>\d+)\s*$/, " https://github.com/logstash-plugins/#{plugin}/issues/\\k<number>[#\\k<number>]") |
| 103 | + line.gsub!(/\[#(?<number>\d+)\]\((?<url>[^)]*)\)/, "[#\\k<number>](\\k<url>)") |
| 104 | + line.gsub!(/^\s+-/, "*") |
| 105 | + report << line |
| 106 | + end |
| 107 | + report << "" |
| 108 | + start_changelog_file.unlink |
| 109 | + end_changelog_file.unlink |
| 110 | +end |
| 111 | + |
| 112 | +release_notes.insert(release_notes_entry_index, report.join("\n").gsub(/\n{3,}/, "\n\n")) |
| 113 | + |
| 114 | +IO.write(RELEASE_NOTES_PATH, release_notes.join("\n")) |
| 115 | + |
| 116 | +if token.nil? |
| 117 | + puts "No token provided, skipping commit and push" |
| 118 | + exit |
| 119 | +end |
| 120 | + |
| 121 | +puts "Creating commit.." |
| 122 | +branch_name = "update_release_notes_#{Time.now.to_i}" |
| 123 | +`git checkout -b #{branch_name}` |
| 124 | +`git commit #{RELEASE_NOTES_PATH} -m "Update release notes for #{current_release}"` |
| 125 | + |
| 126 | +puts "Pushing commit.." |
| 127 | +`git remote set-url origin https://x-access-token:#{token}@github.com/elastic/logstash.git` |
| 128 | +`git push origin #{branch_name}` |
| 129 | + |
| 130 | +puts "Creating Pull Request" |
| 131 | +pr_title = "Release notes for #{current_release}" |
| 132 | +result = `curl -H "Authorization: token #{token}" -d '{"title":"#{pr_title}","base":"#{release_branch}", "head":"#{branch_name}", "draft": true}' https://api.github.com/repos/elastic/logstash/pulls` |
| 133 | +puts result |
| 134 | +pr_number = JSON.parse(result)["number"] |
| 135 | +puts `curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: token #{token}" https://api.github.com/repos/elastic/logstash/issues/#{pr_number}/assignees -d '{"assignees":["#{user}"]}'` |
| 136 | +puts "Done" |
0 commit comments