renogen 1.1.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +155 -25
  3. data/lib/renogen/change_log.rb +1 -0
  4. data/lib/renogen/change_log/item.rb +6 -4
  5. data/lib/renogen/change_log/model.rb +11 -8
  6. data/lib/renogen/change_log/validator.rb +61 -0
  7. data/lib/renogen/change_log/writer.rb +35 -3
  8. data/lib/renogen/cli.rb +8 -4
  9. data/lib/renogen/cli/param_parser.rb +12 -0
  10. data/lib/renogen/config.rb +16 -15
  11. data/lib/renogen/exceptions.rb +5 -0
  12. data/lib/renogen/exceptions/invalid_item_found.rb +32 -0
  13. data/lib/renogen/exceptions/yaml_file_blank.rb +21 -0
  14. data/lib/renogen/exceptions/yaml_file_invalid.rb +20 -0
  15. data/lib/renogen/extraction_stratagies.rb +0 -2
  16. data/lib/renogen/extraction_stratagies/yaml_file.rb +0 -1
  17. data/lib/renogen/extraction_stratagies/yaml_file/parser.rb +5 -4
  18. data/lib/renogen/extraction_stratagies/yaml_file/reader.rb +20 -11
  19. data/lib/renogen/formatters.rb +2 -0
  20. data/lib/renogen/formatters/base.rb +32 -7
  21. data/lib/renogen/formatters/csv.rb +42 -0
  22. data/lib/renogen/formatters/html.rb +1 -1
  23. data/lib/renogen/formatters/markdown.rb +2 -2
  24. data/lib/renogen/formatters/markdown_table.rb +58 -0
  25. data/lib/renogen/generator.rb +9 -1
  26. data/lib/renogen/version.rb +1 -1
  27. data/lib/renogen/writers/csv.rb +23 -0
  28. data/spec/lib/renogen/change_log/item_spec.rb +1 -1
  29. data/spec/lib/renogen/change_log/model_spec.rb +22 -8
  30. data/spec/lib/renogen/change_log/validator_spec.rb +38 -0
  31. data/spec/lib/renogen/change_log/writer_spec.rb +199 -2
  32. data/spec/lib/renogen/config_spec.rb +7 -2
  33. data/spec/lib/renogen/exceptions/invalid_item_found_spec.rb +35 -0
  34. data/spec/lib/renogen/exceptions/stratagy_not_found_spec.rb +2 -1
  35. data/spec/lib/renogen/{extraction_stratagies/yaml_file/exceptions → exceptions}/yaml_file_blank_spec.rb +1 -1
  36. data/spec/lib/renogen/exceptions/yaml_file_invalid_spec.rb +12 -0
  37. data/spec/lib/renogen/extraction_stratagies/yaml_file/parser_spec.rb +3 -3
  38. data/spec/lib/renogen/extraction_stratagies/yaml_file/reader_spec.rb +11 -5
  39. data/spec/lib/renogen/formatters/base_spec.rb +14 -4
  40. data/spec/lib/renogen/formatters/csv_spec.rb +49 -0
  41. data/spec/lib/renogen/formatters/html_spec.rb +1 -1
  42. data/spec/lib/renogen/formatters/markdown_spec.rb +2 -2
  43. data/spec/spec_helper.rb +5 -0
  44. data/spec/support/renogen_helper.rb +9 -0
  45. metadata +20 -11
  46. data/lib/renogen/extraction_stratagies/yaml_file/exceptions.rb +0 -12
  47. data/lib/renogen/extraction_stratagies/yaml_file/exceptions/yaml_file_blank.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 441ccac5869c9d78abc7c3d163a851c4341d29b2
4
- data.tar.gz: 78e083dde9bba0f04c01c9ce4fe37ac6a3c862b6
2
+ SHA256:
3
+ metadata.gz: 882d9056311e5a9baba8dd05bd1ec8c2ada3e5d84d612abbb15940140e07f9ed
4
+ data.tar.gz: ece836f07e37055b4d84180c7c9522d32814e5c1839e2484e4cf2354acff29f2
5
5
  SHA512:
6
- metadata.gz: c5851785f48aed5505e8b1506c9f5752b6c49c24dca5488110f9a28fdc0d02b33b90c399c3db571beb65d77d908892fdb30f6e8f335bd8b701ec9744f3eb7979
7
- data.tar.gz: 96f6024f0e7decc226d44ce9b55d9f746aa3e2a272a0033b82d219558f1396e65207daae4330f40001b23484bc72a0182cdc48b77b237d92595798e8e9a03d25
6
+ metadata.gz: 0d2edd0513ee545a1b26e0f78846deb543e38ba0227e96d92b6497435916563684f61df58673650e12634e647bdae4e0cde547ee38052d6f53b5f4ea01a2d76e
7
+ data.tar.gz: 6395af9fc76aefa6a3f126cc633aeca9eb80330eb6ab95f59be48f04522ead09f13ab3bf70e96eb923d20c7421d26266634aa7b05f5da52d960ef4ee517477c9
data/README.md CHANGED
@@ -2,42 +2,76 @@
2
2
 
3
3
  Renogen or Re(lease) No(tes) Gen(erator) is a development tool to separate feature notes from product versions.
4
4
 
5
-
6
5
  This renogen can not do and will have to be reviewed manually
6
+
7
7
  - Order the notes in the orrect order (e.g. if a task has to be run before/after something else)
8
8
  - Remove Duplicate notes that might be added (e.g. 2 tickets might want to run the same task)
9
9
 
10
+ ## Contents
10
11
 
11
- ### Installation
12
+ - [Installation](#Installation)
13
+ - [Usage](#Usage)
14
+ - [Configuration](#Configuration)
15
+ - [Frequently Asked Questions](#FAQ)
16
+ - [License](#License)
17
+
18
+ <a name="Installation"></a>
19
+ ## Installation
12
20
 
13
21
  To install Renogen, use the following command:
14
22
 
15
- `$ gem install renogen`
23
+ ```
24
+ $ gem install renogen
25
+ ```
26
+
27
+ or add the following to your Gemfile when using Bundler
28
+
29
+ ```
30
+ gem 'renogen', require: false, group: :development
31
+ ```
32
+
33
+ and run
34
+
35
+ ```
36
+ bundle install
37
+ ```
16
38
 
17
- or add the following to your Gemfile
39
+ If you would prefer you can use a prebuild docker image.
18
40
 
19
- `gem 'renogen', :require => false, :group => :development`
41
+ Example:
20
42
 
21
- `$ renogen init # optional Creates directory for notes`
43
+ ```bash
44
+ $ docker run --rm -v `pwd`/.renogen:/.renogen:ro -v `pwd`/change_log:/change_log/ ddazza/renogen <CMD>
45
+ ```
22
46
 
23
- `$ renogen --help # List available options`
47
+ Now, you may initialize your repository with a `change_log` directory and a basic `.renogen` config file:
24
48
 
25
- ### Usage
49
+ ```
50
+ $ renogen init` # optional Creates directory for notes
51
+ ```
52
+
53
+ <a name="Usage"></a>
54
+ ## Usage
26
55
 
27
56
  To generate your notes run the following command
28
57
 
29
- `$ renogen <VERSION> # e.g v1.2.1`
58
+ ```
59
+ $ renogen <VERSION> # e.g v1.2.1
60
+ ```
30
61
 
31
62
  Unfortunatly renogen cant write documentation for your change.
32
63
  By default renogen uses the yaml file stratagy to extract your notes
33
64
 
34
- `$ renogen --help # list available command options`
65
+ ```
66
+ $ renogen --help # list available command options
67
+ ```
68
+
69
+ ### Adding YAML feature notes
35
70
 
36
- #### Adding YAML feature notes
71
+ You can create a new file within the 'next' version folder (default: `change_log/next/`) manually:
37
72
 
38
- Create a new file within the 'next' version folder(default:'change_log/next/')
73
+ Example feature note:
39
74
 
40
- Example feature note
41
75
  ```yaml
42
76
  # change_log/next/example.yml
43
77
  my_formatted_single_line:
@@ -59,34 +93,130 @@ my_list:
59
93
  - e.g. run this as well
60
94
  ```
61
95
 
62
- #### Usage Examples
96
+ You can also use the `new` command to create a feature note YAML file in the 'next' version folder:
63
97
 
64
- Prepend your notes to a changelog file(TODO make command simple)
98
+ ```
99
+ $ renogen new TICKET_NAME # creates ./change_log/next/TICKET_NAME.yml`
100
+ ```
65
101
 
66
- `$ renogen --format markdown v1.2.1 > CHANGELOG.md | cat - CHANGELOG > CHANGELOG.tmp && mv CHANGELOG.tmp CHANGELOG`
102
+ ### Usage Examples
103
+
104
+ Prepend your notes to a changelog file (TODO make command simple)
105
+
106
+ ```
107
+ $ renogen --format markdown v1.2.1 > CHANGELOG.md | cat - CHANGELOG > CHANGELOG.tmp && mv CHANGELOG.tmp CHANGELOG
108
+ ```
67
109
 
68
110
  Writes notes to html file
69
111
 
70
- `$ renogen --format html v1.2.1 > v1_2_1.html`
112
+ ```
113
+ $ renogen --format html v1.2.1 > v1_2_1.html
114
+ ```
115
+
116
+ Writes note to csv file
117
+
118
+ `$ renogen --format csv v1.2.1 > v1_2_1.csv`
71
119
 
72
120
  Print all notes since v1.0.0 as text
73
121
 
74
- `$ renogen --format text -l v1.0.0 v1.2.1`
122
+ ```
123
+ $ renogen --format text -l v1.0.0 v1.2.1
124
+ ```
125
+
126
+ <a name="Configuration"></a>
127
+ ## Configuration
128
+
129
+ Renogen tries to read the file `.renogen` in the working directory to load its configuration. The file is in YAML format
130
+ with the following keys:
131
+
132
+ | key | meaning | default value | configuration example |
133
+ |----------------------|----------|---------------|-----------------------|
134
+ | `single_line_format` | A template for generating a single line of text from a YAML dictionary in a feature note file. Each dictionary key in the template gets replaced by the value for that key. | `summary (see link)` | `[#ticket](https://github.com/renogen/issue/ticket): summary` |
135
+ | `supported_keys` | YAML dictionary keys allowed in dictionary feature notes. Ignored for dictionaries where rules apply. | `%w[identifier summary link]` | |
136
+ | `input_source` | The format of the feature note files. Currently, only `yaml` is supported. | `yaml` | |
137
+ | `output_format` | The default output format when generating release notes. | `markdown` | `html` |
138
+ | `changelog_path` | The directory where the feature note files are stored. | `./change_log` | `./doc/src/changes` |
139
+ | `default_headings` | The headings/group names that will be printed into a feature note file created by the `new` command. Ignored when there is a rule defined for the given file name. | `%w[Summary Detailed Tasks]` | `%w[Title Description Deployment]` |
140
+ | `allowed_values` | Allowed values for default headings. See [Allowed Values](#AllowedValues). | `{}` (none) | |
141
+ | `remove_duplicates` | Remove duplicate items from output release notes. Not applicable when using table formats (e.g`csv` or `markdown_table`). | `false` | |
142
+
143
+ <a name="AllowedValues"></a>
144
+ ## Allowed Values
145
+
146
+ Allowed values enables you to specify a range of allowed values for any default heading. This can be an array, string or
147
+ regular expression:
148
+
149
+ ```yaml
150
+ default_headings:
151
+ - Products
152
+ - Countries
153
+ - User Facing
154
+ Products: !ruby/regexp '/\b(Foo|Bar)\b/'
155
+ Countries: [UK, AUS, FR]
156
+ User Facing: Yes
157
+ ```
158
+
159
+ <a name="FAQ"></a>
160
+ ## Frequently Asked Questions
161
+
162
+ ### Custom formatter
163
+
164
+ You can use your own formatter quite easily.
165
+
166
+ For example, put this in `lib/my_project/renogen_formatter.rb`:
75
167
 
76
- ### Configuration
168
+ ```ruby
169
+ require 'renogen/formatters'
77
170
 
78
- TODO
79
- * How to set configuration with `.renogen` file
80
- * How to change formatted single line
171
+ class MyProject::RenogenFormatter < Renogen::Formatters::Base
172
+ register :xml
173
+
174
+ def write_header(header)
175
+ "<release><header>#{header}</header>"
176
+ end
177
+
178
+ def write_group(group)
179
+ "<group><title>#{group}</title>"
180
+ end
181
+
182
+ def write_group_end
183
+ "</group>"
184
+ end
185
+
186
+ def write_change(change)
187
+ "<change>#{change}</change>"
188
+ end
189
+
190
+ def write_footer(*)
191
+ "</release>"
192
+ end
193
+ end
194
+ ```
195
+
196
+ You have to include that file when running renogen:
197
+
198
+ ```
199
+ $ renogen -I. -Rlib/my_project/renogen_formatter 1.2.3
200
+ ```
201
+
202
+ ### Why does renogen not use renogen
81
203
 
82
- ### Why does renogen not use renogen?
83
204
  The amount of activity and contributes for this project is small and so it is more practical to use a text file.
84
205
 
85
- ### License
206
+ ### How can I run from source
207
+
208
+ ```
209
+ $ git clone git@github.com:DDAZZA/renogen.git
210
+ $ cd ./renogen/
211
+ $ bundle install
212
+ $ bundle exec ./bin/renogen test
213
+ ```
214
+
215
+ ## License
86
216
 
87
217
  Renogen is a programming tool to generate a log of source code changes
88
218
 
89
- Copyright (C) 2015 David Elliott
219
+ Copyright (C) 2015-2020 David Elliott
90
220
 
91
221
  This program is free software; you can redistribute it and/or modify
92
222
  it under the terms of the GNU General Public License as published by
@@ -5,5 +5,6 @@ module Renogen
5
5
  require_relative 'change_log/group'
6
6
  require_relative 'change_log/writer'
7
7
  require_relative 'change_log/model'
8
+ require_relative 'change_log/validator'
8
9
  end
9
10
  end
@@ -3,9 +3,10 @@ module Renogen
3
3
  # Object to represent single change item
4
4
  class Item
5
5
  attr_accessor :change
6
- attr_reader :group_name
6
+ attr_reader :group_name, :ticket_id
7
7
 
8
- def initialize(group_name, change, options={})
8
+ def initialize(ticket_id, group_name, change, options={})
9
+ @ticket_id = ticket_id
9
10
  @group_name = group_name
10
11
  @change = change
11
12
  end
@@ -15,6 +16,7 @@ module Renogen
15
16
  # @return [String]
16
17
  def to_s
17
18
  return '' unless change
19
+
18
20
  case change.class.to_s
19
21
  when 'String'
20
22
  format_multiline(change)
@@ -46,12 +48,12 @@ module Renogen
46
48
  end
47
49
 
48
50
  def format_array(change)
49
- # TODO should return a string
51
+ # TODO: should return a string
50
52
  change
51
53
  end
52
54
 
53
55
  def format_oneline(change)
54
- # TODO Refactor
56
+ # TODO: Refactor
55
57
  string = config.single_line_format.downcase.gsub('\n', '\n ')
56
58
  config.supported_keys.each do |key|
57
59
  string = string.gsub(key, '#{change[\'' + key + '\']}')
@@ -5,20 +5,14 @@ module Renogen
5
5
  # Object to represent a Changelog/release notes
6
6
  class Model
7
7
  attr_reader :items
8
- attr_accessor :version
8
+ attr_accessor :version, :date
9
9
 
10
10
  def initialize(options={})
11
11
  @version = options[:version]
12
+ @date = options[:date] || Date.today
12
13
  @items = []
13
14
  end
14
15
 
15
- # The title for the change log output
16
- #
17
- # @return [String]
18
- def header
19
- "#{version} (#{Date.today})"
20
- end
21
-
22
16
  # @return [Hash<group_name: change>]
23
17
  def groups
24
18
  items.inject({}) do |hash, change|
@@ -28,6 +22,15 @@ module Renogen
28
22
  end
29
23
  end
30
24
 
25
+ # @return [Hash<ticket id: string>]
26
+ def tickets
27
+ items.inject({}) do |hash, change|
28
+ hash[change.ticket_id] ||= {}
29
+ hash[change.ticket_id][change.group_name] = change.to_s
30
+ hash
31
+ end
32
+ end
33
+
31
34
  # Adds a change to the changelog
32
35
  #
33
36
  # @param change [Renogen::ChangeLog::Item]
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renogen
4
+ module ChangeLog
5
+ # Validates the change log
6
+ class Validator
7
+ def initialize(formatter)
8
+ @formatter = formatter
9
+ @validations = formatter.options['allowed_values']
10
+ end
11
+
12
+ # Validates the change log
13
+ #
14
+ # @param changelog [ChangeLog::Model]
15
+ def validate!(changelog)
16
+ validate_headings(changelog)
17
+ end
18
+
19
+ protected
20
+
21
+ attr_reader :formatter, :validations
22
+
23
+ def validate_headings(changelog)
24
+ return if validations.nil?
25
+ return if changelog.items.none? { |item| validations.key?(item.group_name) }
26
+
27
+ validate_properties(changelog)
28
+ end
29
+
30
+ private
31
+
32
+ def validate_properties(changelog)
33
+ invalid_items = []
34
+ validations.each do |heading, values|
35
+ items_to_select = changelog.items.select { |log| log.group_name == heading }
36
+ invalid_values = items_to_select.map do |i|
37
+ changes = changes_to_validate(i.change)
38
+ next changes - values if values.is_a?(Array)
39
+ next unless values.is_a?(Regexp)
40
+
41
+ changes.detect { |c| c !~ values } # return anything that does not match the regexp.
42
+ end
43
+
44
+ invalid_values = invalid_values.flatten.compact.uniq # remove duplicates and nils
45
+ next if invalid_values.empty?
46
+
47
+ invalid_items << { invalid_value: invalid_values, valid_values: values, group_name: heading }
48
+ end
49
+
50
+ invalid_items = invalid_items.flatten
51
+ raise(Renogen::Exceptions::InvalidItemFound, invalid_items) unless invalid_items.empty?
52
+ end
53
+
54
+ def changes_to_validate(change)
55
+ return [change] if change.is_a? String
56
+
57
+ change
58
+ end
59
+ end
60
+ end
61
+ end
@@ -10,9 +10,29 @@ module Renogen
10
10
  #
11
11
  # @param changelog [ChangeLog::Model]
12
12
  def write!(changelog)
13
- puts formatter.write_header(changelog.header)
13
+ puts formatter.write_header(formatter.header(changelog)) unless formatter.write_header(formatter.header(changelog)).nil?
14
+ if formatter.table_formatter?
15
+ write_by_table!(changelog)
16
+ else
17
+ write_by_group!(changelog)
18
+ end
19
+ puts formatter.write_footer(changelog) unless formatter.write_footer(changelog).nil?
20
+ end
21
+
22
+ # Writes out the change log by group
23
+ #
24
+ # @param changelog [ChangeLog::Model]
25
+ def write_by_group!(changelog)
14
26
  output_groups(changelog.groups)
15
- puts formatter.write_footer(changelog)
27
+ end
28
+
29
+ # Writes out the change log by item
30
+ #
31
+ # @param changelog [ChangeLog::Model]
32
+ def write_by_table!(changelog)
33
+ changelog.tickets.each do |_, ticket|
34
+ puts formatter.write_change(ticket)
35
+ end
16
36
  end
17
37
 
18
38
  protected
@@ -30,10 +50,22 @@ module Renogen
30
50
  def output_groups(groups)
31
51
  groups.each do |group, changes|
32
52
  puts formatter.write_group(group)
33
- changes.each { |change| output_change(change) }
53
+ deduped_changes(changes).each { |change| output_change(change) }
34
54
  puts formatter.write_group_end
35
55
  end
36
56
  end
57
+
58
+ def deduped_changes(changes)
59
+ return changes unless config.remove_duplicates
60
+
61
+ changes.uniq { |c| c.to_s }
62
+ end
63
+
64
+ private
65
+
66
+ def config
67
+ Config.instance
68
+ end
37
69
  end
38
70
  end
39
71
  end
@@ -20,26 +20,28 @@ module Renogen
20
20
  source = options['source'] || config_instance.input_source
21
21
  options['changelog_path'] ||= config_instance.changelog_path
22
22
  options['old_version'] ||= config_instance.changelog_path
23
+ options['release_date'] ||= Date.today
24
+ options['allowed_values'] ||= config_instance.validations
23
25
 
24
26
  begin
25
27
  generator = Renogen::Generator.new(version, source, format, options)
26
28
  generator.generate!
27
29
  rescue Renogen::Exceptions::Base => e
28
30
  puts e.message
29
- exit -1
31
+ exit 1
30
32
  end
31
33
  end
32
34
 
33
35
  # Initialize the current working directory with an example change
34
36
  def self.init
35
- # TODO Refactor to use Config.instance.changelog_path
37
+ # TODO: Refactor to use Config.instance.changelog_path
36
38
  Dir.mkdir('./change_log')
37
39
  puts "Created './change_log/'"
38
40
 
39
41
  Dir.mkdir('./change_log/next')
40
42
  puts "Created './change_log/next/'"
41
43
 
42
- File.open("./change_log/next/added_renogen_gem.yml", 'w') do |f|
44
+ File.open('./change_log/next/added_renogen_gem.yml', 'w') do |f|
43
45
  f.write("Summary:\n")
44
46
  f.write(" identifier: renogen\n")
45
47
  f.write(" link: https://github.com/DDAZZA/renogen\n")
@@ -56,7 +58,7 @@ module Renogen
56
58
 
57
59
  puts "Created './change_log/next/added_renogen_gem.yml'"
58
60
 
59
- File.open(".renogen", 'w') do |f|
61
+ File.open('.renogen', 'w') do |f|
60
62
  f.write("changelog_path: './change_log/'\n")
61
63
  end
62
64
  puts "Created '.renogen'"
@@ -66,6 +68,8 @@ module Renogen
66
68
  #
67
69
  # @param ticket_name [String]
68
70
  def self.new_ticket(ticket_name)
71
+ raise 'You need to provide a ticket_name' if ticket_name.nil?
72
+
69
73
  file_path = File.join(Config.instance.changelog_path, 'next', "#{ticket_name}.yml")
70
74
  File.open(file_path, 'w') do |f|
71
75
  Config.instance.default_headings.each do |h|