easy_changelog 0.1.0 → 0.3.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: 3e89d4e58c276528951204d915ae67f2082004de2986bceea7b9645dae3a97bb
4
+ data.tar.gz: b812c744a04357a41f1326d560e9ef34020ecc4c40e0730823c8b489bc5e9801
5
5
  SHA512:
6
- metadata.gz: 41e7ac1c2b79337413818350b4f014ba03e966271538c8b009b867c62509ea8ca16b481c4c34a4b7298e32d2284afb961d82b192ed1308a42cfb0bf87a63f3e6
7
- data.tar.gz: de76095801aca8f3c54323d357e980b20657334c632d3333644324b4d1cdb51a5445fb782a5f9f035564aff816c2a445aa994e3e992805f9369b5f0c08e8e40d
6
+ metadata.gz: c17d7429a5903e0103785cf0567ee085e19dc0f1283db2b6d4c50967cefbfc6aaa68d031c5b4b2714fc772e5c78f4514d29f188de9e877bc211a4e093eb9fcab
7
+ data.tar.gz: 9c8a60a1b48e7a634b3479bd2438a0417e3cf489f149eafda5749db9716d0dc16072e49c33779f41fbf653a4e63bacf747453c1d27645fb2075e47144a979f1e
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
@@ -1,7 +1,21 @@
1
1
  # Changelog
2
2
 
3
3
  ## master (unreleased)
4
+
5
+ ## 0.3.0 (2025-01-22)
6
+ ### New features
7
+
8
+ * [#beb193a](https://github.com/ivan05almeida/easy_changelog/commit/beb193a): Add release count method. ([@ivan05almeida][])
9
+ * [#1623408](https://github.com/ivan05almeida/easy_changelog/commit/1623408): Allow custom body entry. ([@ivan05almeida][])
10
+
11
+ ## 0.2.0 (2025-01-22)
4
12
  ### New features
5
13
 
6
14
  * [#b0faf01](https://github.com/ivan05almeida/easy_changelog/commit/b0faf01): Initialize EasyChangelog gem. ([@ivan05almeida][])
7
15
  * [#1763186](https://github.com/ivan05almeida/easy_changelog/commit/1763186): Update Readme. ([@ivan05almeida][])
16
+ * [#bec422a](https://github.com/ivan05almeida/easy_changelog/commit/bec422a): Automate release changelog entries. ([@ivan05almeida][])
17
+
18
+ ## [0.1.0] - 2025-01-16
19
+
20
+ - Initial release
21
+ [@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.3.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
@@ -11,6 +11,7 @@ class EasyChangelog
11
11
 
12
12
  opts.banner = "Usage: rake changelog:#{type} [options]"
13
13
 
14
+ opts.on('-b', '--body=ARG', 'Changelog Body Entry') { |arg| options[:body] = arg }
14
15
  opts.on('-r', '--ref-id=ARG', 'Ref ID') { |arg| options[:ref_id] = arg }
15
16
  opts.on('-R', '--ref-type=ARG', 'Ref type (issues|pull|commit)') { |arg| options[:ref_type] = arg }
16
17
  opts.on('-t', '--task-id=ARG', 'Task ID') { |arg| options[:task_id] = arg }
@@ -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.3.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
@@ -34,17 +43,25 @@ class EasyChangelog
34
43
  def read_entries
35
44
  entry_paths.to_h { |path| [path, File.read(path)] }
36
45
  end
46
+
47
+ def release_count(pattern)
48
+ File.read(EasyChangelog.configuration.changelog_filename).scan(pattern).size
49
+ end
37
50
  end
38
51
 
39
- def initialize(content: File.read(EasyChangelog.configuration.changelog_filename), entries: EasyChangelog.read_entries)
52
+ attr_reader :header, :entries
53
+
54
+ def initialize(content: File.read(EasyChangelog.configuration.changelog_filename),
55
+ entries: EasyChangelog.read_entries)
40
56
  require 'strscan'
41
57
 
42
58
  parse(content)
43
59
  @entries = entries
44
60
  end
45
61
 
46
- def and_delete!
47
- @entries.each_key { |path| File.delete(path) }
62
+ def merge_and_delete!
63
+ merge!
64
+ delete!
48
65
  end
49
66
 
50
67
  def merge!
@@ -52,6 +69,15 @@ class EasyChangelog
52
69
  self
53
70
  end
54
71
 
72
+ def delete!
73
+ @entries.each_key { |path| File.delete(path) }
74
+ end
75
+
76
+ def release!
77
+ File.write(EasyChangelog.configuration.changelog_filename, release_content)
78
+ self
79
+ end
80
+
55
81
  def unreleased_content
56
82
  entry_map = parse_entries(@entries)
57
83
  merged_map = merge_entries(entry_map)
@@ -64,7 +90,20 @@ class EasyChangelog
64
90
  merged_content << EOF
65
91
  end
66
92
 
93
+ def release_content
94
+ unreleased = unreleased_content
95
+ raise EmptyReleaseError, 'No unreleased content to release' if unreleased.empty?
96
+
97
+ release_message = EasyChangelog.configuration.release_message_template
98
+ release_message = "\n#{release_message}" unless release_message.start_with?("\n")
99
+
100
+ released_content = [@header, release_message, unreleased, @rest.chomp, *new_contributor_lines].join("\n")
101
+ released_content << EOF
102
+ end
103
+
67
104
  def new_contributor_lines
105
+ return [] unless EasyChangelog.configuration.user_signature
106
+
68
107
  unique_contributor_names = contributors.map { |user| format(CONTRIBUTOR, user: user) }.uniq
69
108
 
70
109
  unique_contributor_names.reject { |line| @rest.include?(line) }
@@ -82,7 +121,7 @@ class EasyChangelog
82
121
 
83
122
  def merge_entries(entry_map)
84
123
  all = @unreleased.merge(entry_map) { |_k, v1, v2| v1.concat(v2) }
85
- canonical = EasyChangelog.configuration.type_mapping.values.to_h { |v| [v, nil] }
124
+ canonical = EasyChangelog.configuration.sections.to_h { |v| [v, nil] }
86
125
  canonical.merge(all).compact
87
126
  end
88
127
 
@@ -90,8 +129,15 @@ class EasyChangelog
90
129
  ss = StringScanner.new(content)
91
130
 
92
131
  @header = ss.scan_until(EasyChangelog.configuration.unreleased_header)
93
- @unreleased = parse_release(ss.scan_until(/\n(?=## )/m))
94
- @rest = ss.rest
132
+ unreleased = ss.scan_until(/\n(?=## )/m)
133
+
134
+ if unreleased
135
+ @unreleased = parse_release(unreleased)
136
+ @rest = ss.rest
137
+ else
138
+ @unreleased = parse_release(ss.rest)
139
+ @rest = ''
140
+ end
95
141
  end
96
142
 
97
143
  # @return [Hash<type, Array<String>]]
@@ -110,7 +156,7 @@ class EasyChangelog
110
156
  changes = Hash.new { |h, k| h[k] = [] }
111
157
 
112
158
  path_content_map.each do |path, content|
113
- header = EasyChangelog.configuration.type_mapping.fetch(entry_type(path))
159
+ header = EasyChangelog.configuration.section_for(entry_type(path))
114
160
 
115
161
  changes[header].concat(content.lines.map(&:chomp))
116
162
  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.3.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: