Commit 56000a7c authored by Andrew Newdigate's avatar Andrew Newdigate Committed by Bob Van Landuyt
Browse files

Automatically update the release notes changelog on tag push

This change will automatically create or update the release notes
for each release, on tag push
parent 8c7b100e
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