IssueとPRベースのタスク管理は利用者にとって楽なものだけど、デプロイだけやる人などがまとめて見るにはちと追いづらかったりする。
そんな時にChangelogがしっかりしてると助かるので、バージョニングとマイルストーンの作成に一定の規約を設けて自動生成する運用を試しています。
マイルストーンとPRのルール
自動生成のための要件はこちら。
- バージョン番号のマイルストーンを作成する
- v0.0.1, v1.0.0.rc1 などセマンティックバージョニング風でつける。
- 一旦Gemのバージョンとしてソートするので、
0.0.9
と0.0.10
がひっくり返らない
- 一旦Gemのバージョンとしてソートするので、
- マイルストーンにDescriptionを書いておくと、blockquoteで取り込む。
- v0.0.1, v1.0.0.rc1 などセマンティックバージョニング風でつける。
- 変更は基本的にPRで取り込む
- CHANGELOGに並ぶのはマイルストーンに紐付けたPRのタイトル
- それぞれ差分へのリンク付き
- 変更がIssueであっても、マイルストーンを付けるといちおう並ぶ
- ラベルを活用する
- Bug, FeatureなどつけておくとPRの下に表示される
PRやIssueに対して、次のような感じで対応する。
コードと出力例
Rakefile
を含む出力例は下記Githubリポジトリに置いてます。
higanworks/changelog_from_milestones
Rakeタスク
一応こんな感じ。とてもベタ書き。
ENV['GITHUB_REPO']
でリポジトリ、プライベートの場合はENV['GITHUB_API_TOKEN']
をセットして使います。
Rakefile
require 'bundler'
Bundler.setup
require 'octokit'
require 'sanitize'
# @client = Octokit::Client.new(:access_token => ENV['GITHUB_API_TOKEN'], auto_paginate: true)
@client = Octokit::Client.new(auto_paginate: true)
@milestones = @client.milestones(ENV['GITHUB_REPO'], {state: 'all'})
task :default do
puts File.read('./_templates/header.txt')
titles = create_array_of_milestone_titles
titles.each do |title|
ms = get_milestone_by_title(title)
next unless ms
puts "## [#{ms.title}](#{ms.url}) (#{ms.state})"
puts "created_at: #{ms.created_at.to_s} "
streak = ""
if ms.closed_at
((ms.closed_at - ms.created_at).to_i / 60 / 60 / 24).times {streak << "■"}
puts "streak: #{streak} "
puts "closed_at: #{ms.closed_at.to_s if ms.closed_at}"
end
puts "<blockquote>"
if ms.description.empty?
puts "No Description."
else
puts ms.description
end
puts "</blockquote>"
if ms.respond_to?(:number)
issues = collect_issues_by_ms(ms.number)
issues.each do |issue|
str = "- "
str << Sanitize.fragment(issue.title, Sanitize::Config::RESTRICTED)
if issue.assignee
str << " by #{issue.assignee.login}"
end
if issue.pull_request
str << " at "
str << "[PR-#{issue.number}](#{issue.pull_request.html_url}/files)"
end
puts str
label_cols = issue.labels.map do |label|
%Q{![#{label.name}](https://img.shields.io/badge/L-#{URI.encode(label.name)}-#{label.color}.svg)}
end
puts " - #{label_cols.join(' ')}" unless label_cols.empty?
end
else
puts "- Nothing comment"
end
puts ""
puts "----"
end
puts File.read('./_templates/footer.txt')
end
def create_array_of_milestone_titles
tags = @milestones.map do |a|
begin
Gem::Version.new(a.title.delete('v'))
rescue
next
end
end.sort.reverse
tags.map {|tag| "v" + tag.to_s}
end
def get_milestone_by_title(title)
@milestones.find {|m| m.title == title}
end
def collect_issues_by_ms(ms_number)
@client.issues(ENV['GITHUB_REPO'], {state: 'all', milestone: ms_number})
end
出力例
IssueやPRはサンプルプロジェクト(higanworks/changelog_from_milestones)で実際に作ったので、どんな様子か確認できるはず。
mdとして出力するので、今はプロジェクトのwikiにCHANGELOG.md
を置いて運用中。