Commit de702ca1 authored by Bob Van Landuyt's avatar Bob Van Landuyt 💬
Browse files

Merge branch 'automatic-changelog-generator' into 'master'

Automatically update the release notes changelog on tag push

See merge request gitlab-org/labkit-ruby!26
parents 8c7b100e 56000a7c
Loading
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -23,3 +23,10 @@ deploy:
    - tools/deploy-rubygem.sh
  only:
    - tags

release:
  stage: deploy
  script:
    - tools/update-changelog.rb "${CI_COMMIT_TAG}"
  only:
    - tags
+108 −0
Original line number Diff line number Diff line
#!/usr/bin/env ruby

require "uri"
require "net/http"
require "openssl"
require "json"
require "optparse"

GitlabError = Class.new(StandardError)
GitlabClientError = Class.new(GitlabError)

def gitlab_client(path, method, body = nil)
  url = URI("https://gitlab.com#{path}")

  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true

  request = case method
            when :get
              Net::HTTP::Get.new(url)
            when :post
              Net::HTTP::Post.new(url)
            when :put
              Net::HTTP::Put.new(url)
            else
              raise "Unknown method: #{method}"
            end

  request["content-type"] = "application/json"
  request["PRIVATE-TOKEN"] = ENV["GITLAB_TOKEN"]
  request.body = JSON.dump(body) if body

  response = http.request(request)

  case response
  when Net::HTTPSuccess
    JSON.parse(response.read_body)
  when Net::HTTPClientError
    raise GitlabClientError, "HTTP #{response.code} for #{method} #{url}: #{response.read_body}"
  else
    raise GitLabError, "HTTP #{response.code} for #{method} #{url}: #{response.read_body}"
  end
end

def list_commits_for_tag(tag)
  commits = %x[git rev-list --no-merges $(git describe --tags --abbrev=0 #{tag}^)..#{tag}].lines
  commits.map(&:chomp)
end

def get_mrs_for_commit(commit_id)
  gitlab_client("/api/v4/projects/gitlab-org%2flabkit-ruby/repository/commits/#{commit_id}/merge_requests", :get)
end

def create_release_for_tag(tag, description)
  req_body = { tag_name: tag, description: description }
  gitlab_client("/api/v4/projects/gitlab-org%2flabkit-ruby/releases", :post, req_body)
end

def update_release_for_tag(tag, description)
  req_body = { description: description }
  gitlab_client("/api/v4/projects/gitlab-org%2flabkit-ruby/releases/#{tag}", :put, req_body)
end

def get_unique_mrs_for_tag(tag)
  commit_ids = list_commits_for_tag(tag)
  dup_mrs = commit_ids.map { |commit_id| get_mrs_for_commit(commit_id) }.flatten
  uniq_mrs = dup_mrs.uniq { |mr| mr["iid"] }

  uniq_mrs.sort { |mr| mr["iid"] }.reverse
end

options = {}
OptionParser.new do |opts|
  opts.banner = "Usage: update-changelog.rb [options] tag"

  opts.on("-d", "--[no-]dry-run", "Display only. Do not update release notes.") do |d|
    options[:dry_run] = d
  end
end.parse!

# Check required conditions
unless ARGV.length == 1
  puts optparse
  exit(-1)
end

tag = ARGV.first

changelog = get_unique_mrs_for_tag(tag).map { |mr| "* #{mr["web_url"]}: #{mr["title"]}" }.join("\n")

if changelog.empty?
  raise "No new changes found"
end

changelog = "## Changes in #{tag}\n\n" + changelog

if options[:dry_run]
  puts changelog
  exit
end

begin
  create_release_for_tag(tag, changelog)
  puts "Created release: https://gitlab.com/gitlab-org/labkit-ruby/-/releases##{tag}"
rescue GitlabClientError
  update_release_for_tag(tag, changelog)
  puts "Updated release: https://gitlab.com/gitlab-org/labkit-ruby/-/releases##{tag}"
end