renogen 1.0.1 → 1.3.1

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 +162 -23
  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 +22 -2
  8. data/lib/renogen/cli.rb +28 -7
  9. data/lib/renogen/cli/param_parser.rb +19 -3
  10. data/lib/renogen/config.rb +15 -14
  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 +141 -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 +21 -12
  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: 4f7b33f25cc233f9fe0edee5b01bba4c6cdfb360
4
- data.tar.gz: fead7889665a401452166990a0901346992d118d
2
+ SHA256:
3
+ metadata.gz: 3f61827e5a1a1224d86a20cddecf5757ace8719f2c6e38120e859f8951289814
4
+ data.tar.gz: f1174f4d416402cbf025a5a05f3f2f5ca00c436cecc032c6b87399dac7422bed
5
5
  SHA512:
6
- metadata.gz: 612f38e5e1eae5cccf380394a9c5903883c04b7b7dc557296a145fb9eed530faa1c869263e91a7024ec2e75cbc0288ed2973b78859d5aec9c3bca3f14fcfbe49
7
- data.tar.gz: 70ee850b2a5ca272d256daf6feef1c24960aaf49a24680f3239a1e6823f2369c36f261b39aa73310dbe8df1d3034cc71641f13682d5a6da6bc67c91fe28a8281
6
+ metadata.gz: 6683b5861e4d779967bf100a6683be6c6dccfe64e099906f6991caa266e5a976601c96a9905d8e89d91d2a958190301b3d7c54f79e7a644ff27319f8f50da926
7
+ data.tar.gz: aebbc3dcdf470a7882cf2c038406085d9233774f828675a7b5c0e787899c5be6ccb297419c9a82d92e9e64d172a73dcc71f2a6258aedf893c9dd8e16adbbec3e
data/README.md CHANGED
@@ -2,35 +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
- ### Installation
5
+ This renogen can not do and will have to be reviewed manually
6
+
7
+ - Order the notes in the orrect order (e.g. if a task has to be run before/after something else)
8
+ - Remove Duplicate notes that might be added (e.g. 2 tickets might want to run the same task)
9
+
10
+ ## Contents
11
+
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
6
20
 
7
21
  To install Renogen, use the following command:
8
22
 
9
- `$ 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
+ ```
38
+
39
+ If you would prefer you can use a prebuild docker image.
40
+
41
+ Example:
10
42
 
11
- or add the following to your Gemfile
43
+ ```bash
44
+ $ docker run --rm -v `pwd`/.renogen:/.renogen:ro -v `pwd`/change_log:/change_log/ ddazza/renogen <CMD>
45
+ ```
12
46
 
13
- `gem 'renogen', :require => false, :group => :development`
47
+ Now, you may initialize your repository with a `change_log` directory and a basic `.renogen` config file:
14
48
 
15
- `$ renogen init # optional Creates directory for notes`
16
- `$ renogen --help # List available options
49
+ ```
50
+ $ renogen init` # optional Creates directory for notes
51
+ ```
17
52
 
18
- ### Usage
53
+ <a name="Usage"></a>
54
+ ## Usage
19
55
 
20
56
  To generate your notes run the following command
21
57
 
22
- `$ renogen <VERSION> # e.g v1.2.1`
58
+ ```
59
+ $ renogen <VERSION> # e.g v1.2.1
60
+ ```
23
61
 
24
62
  Unfortunatly renogen cant write documentation for your change.
25
63
  By default renogen uses the yaml file stratagy to extract your notes
26
64
 
27
- `$ renogen --help # list available command options`
65
+ ```
66
+ $ renogen --help # list available command options
67
+ ```
68
+
69
+ ### Adding YAML feature notes
28
70
 
29
- #### Adding YAML feature notes
71
+ You can create a new file within the 'next' version folder (default: `change_log/next/`) manually:
30
72
 
31
- Create a new file within the 'next' version folder(default:'change_log/next/')
73
+ Example feature note:
32
74
 
33
- Example feature note
34
75
  ```yaml
35
76
  # change_log/next/example.yml
36
77
  my_formatted_single_line:
@@ -52,31 +93,129 @@ my_list:
52
93
  - e.g. run this as well
53
94
  ```
54
95
 
55
- #### Usage Examples
96
+ You can also use the `new` command to create a feature note YAML file in the 'next' version folder:
56
97
 
57
- 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
+ ```
101
+
102
+ ### Usage Examples
103
+
104
+ Prepend your notes to a changelog file (TODO make command simple)
58
105
 
59
- `$ renogen --format markdown v1.2.1 > CHANGELOG.md | cat - CHANGELOG > CHANGELOG.tmp && mv CHANGELOG.tmp CHANGELOG`
106
+ ```
107
+ $ renogen --format markdown v1.2.1 > CHANGELOG.md | cat - CHANGELOG > CHANGELOG.tmp && mv CHANGELOG.tmp CHANGELOG
108
+ ```
60
109
 
61
110
  Writes notes to html file
62
111
 
63
- `$ 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`
64
119
 
65
120
  Print all notes since v1.0.0 as text
66
121
 
67
- `$ 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
+
142
+ <a name="AllowedValues"></a>
143
+ ## Allowed Values
144
+
145
+ Allowed values enables you to specify a range of allowed values for any default heading. This can be an array, string or
146
+ regular expression:
147
+
148
+ ```yaml
149
+ default_headings:
150
+ - Products
151
+ - Countries
152
+ - User Facing
153
+ Products: !ruby/regexp '/\b(Foo|Bar)\b/'
154
+ Countries: [UK, AUS, FR]
155
+ User Facing: Yes
156
+ ```
157
+
158
+ <a name="FAQ"></a>
159
+ ## Frequently Asked Questions
160
+
161
+ ### Custom formatter
162
+
163
+ You can use your own formatter quite easily.
164
+
165
+ For example, put this in `lib/my_project/renogen_formatter.rb`:
68
166
 
69
- ### Configuration
167
+ ```ruby
168
+ require 'renogen/formatters'
70
169
 
71
- TODO
72
- * How to set configuration with `.renogen` file
73
- * How to change formatted single line
170
+ class MyProject::RenogenFormatter < Renogen::Formatters::Base
171
+ register :xml
172
+
173
+ def write_header(header)
174
+ "<release><header>#{header}</header>"
175
+ end
176
+
177
+ def write_group(group)
178
+ "<group><title>#{group}</title>"
179
+ end
180
+
181
+ def write_group_end
182
+ "</group>"
183
+ end
184
+
185
+ def write_change(change)
186
+ "<change>#{change}</change>"
187
+ end
188
+
189
+ def write_footer(*)
190
+ "</release>"
191
+ end
192
+ end
193
+ ```
194
+
195
+ You have to include that file when running renogen:
196
+
197
+ ```
198
+ $ renogen -I. -Rlib/my_project/renogen_formatter 1.2.3
199
+ ```
200
+
201
+ ### Why does renogen not use renogen
202
+
203
+ The amount of activity and contributes for this project is small and so it is more practical to use a text file.
204
+
205
+ ### How can I run from source
206
+
207
+ ```
208
+ $ git clone git@github.com:DDAZZA/renogen.git
209
+ $ cd ./renogen/
210
+ $ bundle install
211
+ $ bundle exec ./bin/renogen test
212
+ ```
74
213
 
75
- ### License
214
+ ## License
76
215
 
77
216
  Renogen is a programming tool to generate a log of source code changes
78
217
 
79
- Copyright (C) 2015 David Elliott
218
+ Copyright (C) 2015-2020 David Elliott
80
219
 
81
220
  This program is free software; you can redistribute it and/or modify
82
221
  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
@@ -10,32 +10,38 @@ module Renogen
10
10
  # @param args [Array]
11
11
  def self.run(args)
12
12
  return init if args.first == 'init'
13
+ return new_ticket(args[1]) if args.first == 'new'
14
+
13
15
  param_parser = ParamParser.new(args)
14
16
  version, options = param_parser.parse
17
+ config_instance = Config.instance
15
18
 
16
- format = options['format'] || Config.instance.output_format
17
- source = options['source'] || Config.instance.input_source
18
- options['changelog_path'] ||= Config.instance.changelog_path
19
- options['old_version'] ||= Config.instance.changelog_path
19
+ format = options['format'] || config_instance.output_format
20
+ source = options['source'] || config_instance.input_source
21
+ options['changelog_path'] ||= config_instance.changelog_path
22
+ options['old_version'] ||= config_instance.changelog_path
23
+ options['release_date'] ||= Date.today
24
+ options['allowed_values'] ||= config_instance.validations
20
25
 
21
26
  begin
22
27
  generator = Renogen::Generator.new(version, source, format, options)
23
28
  generator.generate!
24
29
  rescue Renogen::Exceptions::Base => e
25
30
  puts e.message
26
- exit -1
31
+ exit 1
27
32
  end
28
33
  end
29
34
 
30
35
  # Initialize the current working directory with an example change
31
36
  def self.init
37
+ # TODO: Refactor to use Config.instance.changelog_path
32
38
  Dir.mkdir('./change_log')
33
39
  puts "Created './change_log/'"
34
40
 
35
41
  Dir.mkdir('./change_log/next')
36
42
  puts "Created './change_log/next/'"
37
43
 
38
- 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|
39
45
  f.write("Summary:\n")
40
46
  f.write(" identifier: renogen\n")
41
47
  f.write(" link: https://github.com/DDAZZA/renogen\n")
@@ -52,10 +58,25 @@ module Renogen
52
58
 
53
59
  puts "Created './change_log/next/added_renogen_gem.yml'"
54
60
 
55
- File.open(".renogen", 'w') do |f|
61
+ File.open('.renogen', 'w') do |f|
56
62
  f.write("changelog_path: './change_log/'\n")
57
63
  end
58
64
  puts "Created '.renogen'"
59
65
  end
66
+
67
+ # Creates a new template file
68
+ #
69
+ # @param ticket_name [String]
70
+ def self.new_ticket(ticket_name)
71
+ raise 'You need to provide a ticket_name' if ticket_name.nil?
72
+
73
+ file_path = File.join(Config.instance.changelog_path, 'next', "#{ticket_name}.yml")
74
+ File.open(file_path, 'w') do |f|
75
+ Config.instance.default_headings.each do |h|
76
+ f.write("#{h}:\n")
77
+ end
78
+ end
79
+ puts "Created '#{file_path}'"
80
+ end
60
81
  end
61
82
  end