easy_changelog 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 236711ac036de2a58700baeceed154cd1bd88366572c6079035709e21d998704
4
- data.tar.gz: 48440bccef6c26da9dab7b7ca2194af70a3f2880eb4bd4d540d5af4ab63161f8
3
+ metadata.gz: 21835b5eb789e18516da3e0999774650180baa9fa09dcc6c5673089b179d2ce9
4
+ data.tar.gz: 4bbdd3915868d1795114f24139af6989e6d0a24b636ae0eb7b3b5ed2f5e90cf1
5
5
  SHA512:
6
- metadata.gz: 41e7ac1c2b79337413818350b4f014ba03e966271538c8b009b867c62509ea8ca16b481c4c34a4b7298e32d2284afb961d82b192ed1308a42cfb0bf87a63f3e6
7
- data.tar.gz: de76095801aca8f3c54323d357e980b20657334c632d3333644324b4d1cdb51a5445fb782a5f9f035564aff816c2a445aa994e3e992805f9369b5f0c08e8e40d
6
+ metadata.gz: deb7ac871213f284a3d3e99fff0b20172038b6e946b96d6f0b2d076599673c21df58330841c96925f985a7992fb4ae6e78fd85e4e5a5f8ccde7d5e09039ca4a9
7
+ data.tar.gz: 5130b516e4e1207bac5dc5b7be5e05d3279dd03aa94e106d44ce8b7b28446385b9fcfcfd9b263f4a9a93129cf29e939b6c5cc3499af3af5125d580ab107847c0
data/.rubocop.yml CHANGED
@@ -2,6 +2,29 @@ AllCops:
2
2
  TargetRubyVersion: 2.6
3
3
  NewCops: enable
4
4
 
5
+ Lint/EmptyBlock:
6
+ Exclude:
7
+ - 'lib/easy_changelog/task_options_parser.rb'
8
+ Metrics/AbcSize:
9
+ Exclude:
10
+ - 'lib/easy_changelog/task_options_parser.rb'
11
+
12
+ Metrics/BlockLength:
13
+ Exclude:
14
+ - 'lib/easy_changelog/tasks/changelog.rake'
15
+
16
+ Metrics/ClassLength:
17
+ Enabled: false
18
+
19
+ Metrics/MethodLength:
20
+ Exclude:
21
+ - 'lib/easy_changelog/configuration.rb'
22
+ - 'lib/easy_changelog/task_options_parser.rb'
23
+
24
+ Metrics/ParameterLists:
25
+ Exclude:
26
+ - 'lib/easy_changelog/entry.rb'
27
+
5
28
  Style/Documentation:
6
29
  Enabled: false
7
30
 
data/CHANGELOG.md CHANGED
@@ -5,3 +5,9 @@
5
5
 
6
6
  * [#b0faf01](https://github.com/ivan05almeida/easy_changelog/commit/b0faf01): Initialize EasyChangelog gem. ([@ivan05almeida][])
7
7
  * [#1763186](https://github.com/ivan05almeida/easy_changelog/commit/1763186): Update Readme. ([@ivan05almeida][])
8
+ * [#bec422a](https://github.com/ivan05almeida/easy_changelog/commit/bec422a): Automate release changelog entries. ([@ivan05almeida][])
9
+
10
+ ## [0.1.0] - 2025-01-16
11
+
12
+ - Initial release
13
+ [@ivan05almeida]: https://github.com/ivan05almeida
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- easy_changelog (0.1.0)
4
+ easy_changelog (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/Rakefile CHANGED
@@ -13,9 +13,7 @@ require 'rubocop/rake_task'
13
13
 
14
14
  RuboCop::RakeTask.new
15
15
 
16
-
17
16
  path = File.expand_path(__dir__)
18
17
  Dir.glob("#{path}/lib/easy_changelog/tasks/**/*.rake").each { |f| import f }
19
18
 
20
-
21
19
  task default: %i[test rubocop]
@@ -1,12 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'dotenv/load'
4
+ require 'date'
4
5
 
5
6
  class EasyChangelog
6
7
  class Configuration
7
- attr_accessor :changelog_filename, :main_branch, :filename_max_length, :include_empty_task_id, :tasks_url
8
- attr_reader :entries_path, :unreleased_header, :entry_path_format, :user_signature, :type_mapping
9
- attr_writer :repo_url, :tasks_url
8
+ attr_accessor :changelog_filename, :main_branch, :filename_max_length, :include_empty_task_id, :tasks_url,
9
+ :task_id_sanitizer
10
+ attr_reader :entries_path, :unreleased_header, :entry_path_format, :user_signature, :type_mapping, :task_id_regex
11
+ attr_writer :repo_url, :release_message_template
10
12
 
11
13
  def initialize
12
14
  @entries_path = 'changelog/'
@@ -14,15 +16,21 @@ class EasyChangelog
14
16
 
15
17
  @main_branch = 'master'
16
18
  @entry_path_format = '<type>_<name>_<timestamp>.md'
17
- @unreleased_header = /#{Regexp.escape("## #{@main_branch} (unreleased)")}/m
19
+ @unreleased_header = /## #{Regexp.escape("#{@main_branch} (unreleased)")}/m
18
20
  @user_signature = Regexp.new(format(Regexp.escape('[@%<user>s][]'), user: '([\w-]+)'))
19
21
 
20
22
  @filename_max_length = 50
21
- @type_mapping = { new: 'New features', fix: 'Bug fixes' }
23
+ @type_mapping = {
24
+ breaking: { title: 'Breaking Changes', level: :major },
25
+ new: { title: 'New features', level: :minor },
26
+ fix: { title: 'Bug fixes', level: :patch }
27
+ }
22
28
  @include_empty_task_id = false
23
29
 
24
30
  @repo_url = ENV.fetch('REPOSITORY_URL', nil)
25
31
  @tasks_url = ENV.fetch('TASKS_URL', nil)
32
+ @task_id_regex = %r{(?<task_id>[^/]+)/(?:.+)}
33
+ @release_message_template = -> { "## #{EasyChangelog::VERSION} (#{Date.today.iso8601})" }
26
34
  end
27
35
 
28
36
  def repo_url
@@ -31,8 +39,24 @@ class EasyChangelog
31
39
  @repo_url
32
40
  end
33
41
 
42
+ def release_message_template
43
+ raise ConfigurationError, 'release_message_template must be set' unless @release_message_template
44
+
45
+ return @release_message_template unless @release_message_template.respond_to?(:call)
46
+
47
+ message = @release_message_template.call
48
+ message = "## #{message}" unless message.start_with?('## ')
49
+ message
50
+ end
51
+
52
+ def task_id_regex=(value)
53
+ raise ArgumentError, 'task_id_regex must be a Regexp' unless value.is_a?(Regexp)
54
+
55
+ @task_id_regex = value
56
+ end
57
+
34
58
  def unreleased_header=(value)
35
- @unreleased_header = /#{Regexp.escape(value)}/m
59
+ @unreleased_header = /## #{Regexp.escape(value)}/m
36
60
  end
37
61
 
38
62
  def entries_path=(value)
@@ -57,6 +81,14 @@ class EasyChangelog
57
81
  @type_mapping.keys
58
82
  end
59
83
 
84
+ def sections
85
+ @type_mapping.values.map { |v| v[:title] }
86
+ end
87
+
88
+ def section_for(type)
89
+ @type_mapping[type][:title]
90
+ end
91
+
60
92
  def entry_path_match_regexp
61
93
  formula = @entry_path_format.gsub(/<(\w+)>/) do |match|
62
94
  matcher = match == '<type>' ? '[^_]' : '.'
@@ -7,6 +7,8 @@ class EasyChangelog
7
7
  id, body = extract_id(body)
8
8
  ref_id ||= id || last_commit_id
9
9
  ref_type ||= id ? :pull : :commit
10
+ task_id ||= discover_task_id
11
+
10
12
  super
11
13
  end
12
14
 
@@ -56,6 +58,15 @@ class EasyChangelog
56
58
  `git log -n1 --format="%h"`.chomp
57
59
  end
58
60
 
61
+ def discover_task_id
62
+ return if EasyChangelog.configuration.task_id_regex.nil?
63
+
64
+ branch_name = `git rev-parse --abbrev-ref HEAD`
65
+ return if branch_name == EasyChangelog.configuration.main_branch
66
+
67
+ EasyChangelog.configuration.task_id_regex.match(branch_name)&.named_captures&.fetch('task_id', nil)
68
+ end
69
+
59
70
  def extract_id(body)
60
71
  /^\[Fix(?:es)? #(\d+)\] (.*)/.match(body)&.captures || [nil, body]
61
72
  end
@@ -22,7 +22,7 @@ namespace :changelog do
22
22
  task :merge do
23
23
  raise 'No entries!' unless EasyChangelog.pending?
24
24
 
25
- EasyChangelog.new.merge!.and_delete!
25
+ EasyChangelog.merge_and_delete!
26
26
  cmd = "git commit -a -m 'Update Changelog'"
27
27
  puts cmd
28
28
  sh cmd
@@ -36,4 +36,12 @@ namespace :changelog do
36
36
  puts 'Do `bundle exec rake changelog:merge`'
37
37
  exit(1)
38
38
  end
39
+
40
+ desc 'Add release entry'
41
+ task :release do
42
+ EasyChangelog.release!
43
+ cmd = "git commit -a -m 'Update Changelog'"
44
+ puts cmd
45
+ sh cmd
46
+ end
39
47
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class EasyChangelog
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -11,6 +11,7 @@ class EasyChangelog
11
11
 
12
12
  class Error < StandardError; end
13
13
  class ConfigurationError < StandardError; end
14
+ class EmptyReleaseError < StandardError; end
14
15
 
15
16
  require 'easy_changelog/railtie' if defined?(Rails)
16
17
 
@@ -27,6 +28,14 @@ class EasyChangelog
27
28
  entry_paths.any?
28
29
  end
29
30
 
31
+ def merge_and_delete!
32
+ new.merge_and_delete!
33
+ end
34
+
35
+ def release!
36
+ new.release!
37
+ end
38
+
30
39
  def entry_paths
31
40
  Dir["#{EasyChangelog.configuration.entries_path}*"]
32
41
  end
@@ -36,15 +45,19 @@ class EasyChangelog
36
45
  end
37
46
  end
38
47
 
39
- def initialize(content: File.read(EasyChangelog.configuration.changelog_filename), entries: EasyChangelog.read_entries)
48
+ attr_reader :header, :entries
49
+
50
+ def initialize(content: File.read(EasyChangelog.configuration.changelog_filename),
51
+ entries: EasyChangelog.read_entries)
40
52
  require 'strscan'
41
53
 
42
54
  parse(content)
43
55
  @entries = entries
44
56
  end
45
57
 
46
- def and_delete!
47
- @entries.each_key { |path| File.delete(path) }
58
+ def merge_and_delete!
59
+ merge!
60
+ delete!
48
61
  end
49
62
 
50
63
  def merge!
@@ -52,6 +65,15 @@ class EasyChangelog
52
65
  self
53
66
  end
54
67
 
68
+ def delete!
69
+ @entries.each_key { |path| File.delete(path) }
70
+ end
71
+
72
+ def release!
73
+ File.write(EasyChangelog.configuration.changelog_filename, release_content)
74
+ self
75
+ end
76
+
55
77
  def unreleased_content
56
78
  entry_map = parse_entries(@entries)
57
79
  merged_map = merge_entries(entry_map)
@@ -64,7 +86,20 @@ class EasyChangelog
64
86
  merged_content << EOF
65
87
  end
66
88
 
89
+ def release_content
90
+ unreleased = unreleased_content
91
+ raise EmptyReleaseError, 'No unreleased content to release' if unreleased.empty?
92
+
93
+ release_message = EasyChangelog.configuration.release_message_template
94
+ release_message = "\n#{release_message}" unless release_message.start_with?("\n")
95
+
96
+ released_content = [@header, release_message, unreleased, @rest.chomp, *new_contributor_lines].join("\n")
97
+ released_content << EOF
98
+ end
99
+
67
100
  def new_contributor_lines
101
+ return [] unless EasyChangelog.configuration.user_signature
102
+
68
103
  unique_contributor_names = contributors.map { |user| format(CONTRIBUTOR, user: user) }.uniq
69
104
 
70
105
  unique_contributor_names.reject { |line| @rest.include?(line) }
@@ -82,7 +117,7 @@ class EasyChangelog
82
117
 
83
118
  def merge_entries(entry_map)
84
119
  all = @unreleased.merge(entry_map) { |_k, v1, v2| v1.concat(v2) }
85
- canonical = EasyChangelog.configuration.type_mapping.values.to_h { |v| [v, nil] }
120
+ canonical = EasyChangelog.configuration.sections.to_h { |v| [v, nil] }
86
121
  canonical.merge(all).compact
87
122
  end
88
123
 
@@ -90,8 +125,15 @@ class EasyChangelog
90
125
  ss = StringScanner.new(content)
91
126
 
92
127
  @header = ss.scan_until(EasyChangelog.configuration.unreleased_header)
93
- @unreleased = parse_release(ss.scan_until(/\n(?=## )/m))
94
- @rest = ss.rest
128
+ unreleased = ss.scan_until(/\n(?=## )/m)
129
+
130
+ if unreleased
131
+ @unreleased = parse_release(unreleased)
132
+ @rest = ss.rest
133
+ else
134
+ @unreleased = parse_release(ss.rest)
135
+ @rest = ''
136
+ end
95
137
  end
96
138
 
97
139
  # @return [Hash<type, Array<String>]]
@@ -110,7 +152,7 @@ class EasyChangelog
110
152
  changes = Hash.new { |h, k| h[k] = [] }
111
153
 
112
154
  path_content_map.each do |path, content|
113
- header = EasyChangelog.configuration.type_mapping.fetch(entry_type(path))
155
+ header = EasyChangelog.configuration.section_for(entry_type(path))
114
156
 
115
157
  changes[header].concat(content.lines.map(&:chomp))
116
158
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: easy_changelog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan de Paula Almeida Filho
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-17 00:00:00.000000000 Z
11
+ date: 2025-01-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Changelog generator, based on Rubocop contributing section.
14
14
  email: