Module: GitDeploy::Changelog
- Defined in:
- lib/git_deploy/changelog.rb
Constant Summary collapse
- HEADER =
<<~HEADER # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). HEADER
- UNRELEASED =
'## [Unreleased]'- CATEGORY_FOR =
{ 'feat' => 'Added', 'fix' => 'Fixed', 'docs' => 'Changed', 'perf' => 'Changed' }.freeze
- CATEGORY_ORDER =
['Added', 'Changed', 'Fixed', 'Removed', 'Deprecated', 'Security'].freeze
Class Method Summary collapse
- .categorize(subject) ⇒ Object
- .extract_section(content, version) ⇒ Object
- .format_entry(subject) ⇒ Object
- .gemspec_version(path = 'git-deploy-ng.gemspec') ⇒ Object
- .git_log_subjects(since_ref, until_ref: 'HEAD') ⇒ Object
- .group_commits(subjects) ⇒ Object
- .latest_tag ⇒ Object
- .render_section(version, date, groups) ⇒ Object
- .update_content(content, version, date:, groups:) ⇒ Object
- .update_file(path, version, date: Date.today.iso8601, since_ref: latest_tag) ⇒ Object
- .verify_version!(content, version) ⇒ Object
Class Method Details
.categorize(subject) ⇒ Object
33 34 35 36 37 38 |
# File 'lib/git_deploy/changelog.rb', line 33 def categorize(subject) type = subject[/\A(\w+)(?:\([^)]+\))?!?:/, 1] return nil unless type CATEGORY_FOR[type] end |
.extract_section(content, version) ⇒ Object
71 72 73 74 75 76 77 78 |
# File 'lib/git_deploy/changelog.rb', line 71 def extract_section(content, version) pattern = /^## \[#{Regexp.escape(version)}\][^\n]*\n(.*?)(?=^## \[|\z)/m match = content.match(pattern) return nil unless match body = match[1].strip body.empty? ? nil : body end |
.format_entry(subject) ⇒ Object
40 41 42 43 44 45 |
# File 'lib/git_deploy/changelog.rb', line 40 def format_entry(subject) body = subject.sub(/\A\w+(?:\([^)]+\))?!?:\s*/, '') breaking = subject.include?('!:') || subject.match?(/\A\w+!:/) entry = body.empty? ? subject : body[0].upcase + body[1..] breaking ? "**BREAKING:** #{entry}" : entry end |
.gemspec_version(path = 'git-deploy-ng.gemspec') ⇒ Object
28 29 30 31 |
# File 'lib/git_deploy/changelog.rb', line 28 def gemspec_version(path = 'git-deploy-ng.gemspec') line = File.readlines(path).find { |l| l =~ /gem\.version\s*=/ } line[/['"]([^'"]+)['"]/, 1] end |
.git_log_subjects(since_ref, until_ref: 'HEAD') ⇒ Object
105 106 107 108 109 110 111 |
# File 'lib/git_deploy/changelog.rb', line 105 def git_log_subjects(since_ref, until_ref: 'HEAD') range = since_ref ? "#{since_ref}..#{until_ref}" : until_ref `git log #{range} --pretty=format:%s --no-merges`. force_encoding(Encoding::UTF_8). split("\n", -1). reject(&:empty?) end |
.group_commits(subjects) ⇒ Object
47 48 49 50 51 52 53 54 55 56 |
# File 'lib/git_deploy/changelog.rb', line 47 def group_commits(subjects) groups = Hash.new { |h, k| h[k] = [] } subjects.each do |subject| next if subject.start_with?('Merge ') category = categorize(subject) || next groups[category] << format_entry(subject) end groups.each_value(&:uniq!) groups end |
.latest_tag ⇒ Object
113 114 115 116 |
# File 'lib/git_deploy/changelog.rb', line 113 def latest_tag tag = `git describe --tags --abbrev=0 2>/dev/null`.strip tag.empty? ? nil : tag end |
.render_section(version, date, groups) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/git_deploy/changelog.rb', line 58 def render_section(version, date, groups) lines = ["## [#{version}] - #{date}", ''] CATEGORY_ORDER.each do |category| entries = groups[category] next if entries.nil? || entries.empty? lines << "### #{category}" entries.sort.each { |entry| lines << "- #{entry}" } lines << '' end lines.join("\n").rstrip + "\n" end |
.update_content(content, version, date:, groups:) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/git_deploy/changelog.rb', line 86 def update_content(content, version, date:, groups:) section = render_section(version, date, groups) if content.nil? || content.dup.force_encoding(Encoding::UTF_8).strip.empty? return HEADER + "\n#{UNRELEASED}\n\n" + section + "\n" end content = content.dup.force_encoding(Encoding::UTF_8) unless content.include?(UNRELEASED) abort "Error: CHANGELOG.md is missing the #{UNRELEASED} heading" end if content.match?(/^## \[#{Regexp.escape(version)}\]/m) abort "Error: CHANGELOG.md already has a section for version #{version}" end content.sub("#{UNRELEASED}\n", "#{UNRELEASED}\n\n#{section}\n") end |
.update_file(path, version, date: Date.today.iso8601, since_ref: latest_tag) ⇒ Object
118 119 120 121 122 123 |
# File 'lib/git_deploy/changelog.rb', line 118 def update_file(path, version, date: Date.today.iso8601, since_ref: latest_tag) subjects = git_log_subjects(since_ref) groups = group_commits(subjects) content = File.exist?(path) ? File.read(path) : nil File.write(path, update_content(content, version, date: date, groups: groups)) end |
.verify_version!(content, version) ⇒ Object
80 81 82 83 84 |
# File 'lib/git_deploy/changelog.rb', line 80 def verify_version!(content, version) section = extract_section(content, version) abort "Error: CHANGELOG.md has no entry for version #{version}" if section.nil? section end |