release-notes 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 691d3c472f9792ce0e682085a84f2207f6a4fdda
4
+ data.tar.gz: bb273e2057859b3a70ffcf692ed6f442152df7d9
5
+ SHA512:
6
+ metadata.gz: e056b89f89730bb42537f613e7dcb23363ee4ff25c74aecb54711882935cd2f29912b0a86ef4e3a4af7ea11d5012c55c371033f661b0248ec224b2bd0fb9601b
7
+ data.tar.gz: ed86f0d78b8f71c11e8f39766a3e92f403da70c6711e933b375d1c84c061bfffc903c325ea9b494395b817eff227bf5a36933256cf5bab09538c78ebe44a91c1
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ RELEASE_NOTES.md
12
+ release-notes.tmp.md
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.4.0
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.12.5
6
+ env:
7
+ global:
8
+ secure: ovawk+NnGZGW+hOeC3iPMZ9jKY5FvaxfHLj6XX4Vfe12cq0cdGYirW6pk2uKdJZdE0pC5q4sFv1ZTgcNSTySuNpQ9Gkp4xjcGEVr5JSdmVse8DuxttwkeSPt94DemyMK3f0A3dWN8OJF7/NQbacPeUuXryxmusCqJMlWccVYGeSQQNJ21mFfl2pCf6Z9qls4Q3rzXc4hsVQ5G1B/j9YcMjax0EwXI4cEq/6JadK4OmXrGkCw6M/wvBEqNyhLz+R/gcYiJZqYUETdncRuNiH8N6JKiaCnZUFkGY4bggpOvz29lj3JEQV9jY+3UBeqxuYj3glT7buEuf6//ARedY7IDlupkiDNuSpIKJhzIKAgitc/g+cnUflQV8PiImAEseO0OpoAJ7Bsa688EgyvMgo/kYwjHqZetvlUksQI+XyZ+o7/88LF2Qz4pbuBtdFC4DaKaeqQN5afIHSPtxhYwFA/n+GmY4jymj5ZWod9zZasVdlDgCQzgH5M1W7nEGun8mRW93wf8+3wAx+fXVbB67aDbqJFBJfH6zm/aBCN16kGkHUibRxUyb0F52woBQLUcOIr9Kbujp02D7IK3Z7PFz+DtHfA8aYRXxRDIHxx0tw91UxQzBinsq27kH8EWDmEKMHJeZlMRR4X4ijd6+KFoKUobXFblCeVeO0HCIBxpfTc39w=
9
+ after_success:
10
+ - bundle exec codeclimate-test-reporter
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --protected
2
+ --private
3
+ --hide-api private
4
+ --exclude templates
5
+ --markup markdown
6
+ --markup-provider redcarpet
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at dvmonroe6@gmail.com. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in release-notes.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'aruba', '~> 0.14.2'
8
+ gem 'codeclimate-test-reporter', require: false
9
+ gem 'pry-byebug', '~> 3.4.2'
10
+ gem 'rspec', '~> 3.6.0'
11
+ gem 'simplecov', require: false
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Drew Monroe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # Release::Notes
2
+
3
+ [![Build Status](https://travis-ci.org/dvmonroe/release-notes.svg?branch=master)](https://travis-ci.org/dvmonroe/release-notes)
4
+ [![Code Climate](https://codeclimate.com/github/dvmonroe/release-notes/badges/gpa.svg)](https://codeclimate.com/github/dvmonroe/release-notes)
5
+ [![Test Coverage](https://codeclimate.com/github/dvmonroe/release-notes/badges/coverage.svg)](https://codeclimate.com/github/dvmonroe/release-notes/coverage)
6
+ [![Inline docs](http://inch-ci.org/github/dvmonroe/release-notes.svg?branch=master)](http://inch-ci.org/github/dvmonroe/release-notes)
7
+
8
+ Release notes for the stakeholders.
9
+
10
+ Release::Notes is a small wrapper around your project's git log. The gem is
11
+ intended to help increase visability to all team members and stakeholders with
12
+ automated documentation of important changes made to your code base for a given production
13
+ deployment based on tags and labels in your commit messages.
14
+
15
+ Release::Notes is different than a changelog. It's meant for situations where other
16
+ team members (non-devs) in your organization need to know about key changes
17
+ to the production software. If your looking for a comprehnsive changelog that
18
+ reflects resolved github issues or logging all merges to your project, I would
19
+ suggest you look at something else like
20
+ [github-changelog-generator](https://github.com/skywinder/github-changelog-generator).
21
+
22
+ Not looking for a tested gem or prefer the rawness of a bash script? Checkout the similar
23
+ [bash implementation](https://gist.github.com/dvmonroe/300226a1ed4435fb38d72e72e1bbc5a0)
24
+
25
+ ## Getting Started
26
+
27
+ Add this line to your application's Gemfile:
28
+
29
+ ```ruby
30
+ gem 'release-notes'
31
+ ```
32
+
33
+ And then execute:
34
+
35
+ ```sh
36
+ $ bundle
37
+ ```
38
+
39
+ Or install it yourself as:
40
+
41
+ ```sh
42
+ $ gem install release-notes
43
+ ```
44
+
45
+ After you install Release::Notes, if you're using with rails, you can run the generator:
46
+
47
+ ```sh
48
+ $ rails generate release:notes:install
49
+ ```
50
+
51
+ This Release::Notes generator creates an initializer file to allow further configuration.
52
+
53
+ If you're not in a rails project you can create the file yourself.
54
+
55
+
56
+ ## Configure
57
+
58
+ Override any of these defaults in `config/initializers/release_notes.rb`:
59
+
60
+ ```ruby
61
+ Release::Notes.configure do |config|
62
+ config.output_file = './RELEASE_NOTES.md'
63
+ config.temp_file = './release-notes.tmp.md'
64
+ config.include_merges = false
65
+ config.ignore_case = true
66
+ config.log_format = '- %s'
67
+ config.extended_regex = true
68
+ config.bug_labels = %w[Fix Update]
69
+ config.feature_labels = %w[Add Create]
70
+ config.misc_labels = %w[Refactor]
71
+ config.bug_title = '**Fixed bugs:**'
72
+ config.feature_title = '**Implemented enhancements:**'
73
+ config.misc_title = '**Miscellaneous:**'
74
+ config.link_to_labels = %w[]
75
+ config.link_to_humanize = %w[]
76
+ config.link_to_sites = %w[]
77
+ config.timezone = 'America/New_York'
78
+ end
79
+ ```
80
+
81
+ For more information about each individual setting checkout Release::Notes's
82
+ [config docs](http://www.rubydoc.info/github/dvmonroe/release-notes/master/Release/Notes/Configuration).
83
+
84
+ ## Usage
85
+
86
+ ### TL;DR
87
+
88
+ ```sh
89
+ bundle exec update_release_notes:run
90
+ ```
91
+
92
+ ### Git Worklow
93
+
94
+ Release::Notes works best with a rebase workflow and requires tagging. General rebase benefits include:
95
+
96
+ * One clear commit per feature, bug or miscellaneous addition to the codebase
97
+ * Commits in logical time manner
98
+
99
+ By default configuration, Release::Notes ignores merges. Along with rebasing, by deafult,
100
+ Release::Notes relies mainly on the subject of a commit. Therefore, it's important to craft concise and
101
+ meaningful commit subjects with longer bodies as needed for larger feature additions or bug fixes.
102
+
103
+ For more information about a rebase workflow or crafting solid commit messages
104
+ check out the following links
105
+
106
+ * [Commit Messages](http://chris.beams.io/posts/git-commit/)
107
+ * [Git Rebase Workflow](https://git-scm.com/book/en/v2/Git-Branching-Rebasing)
108
+
109
+ ### Deploying with Capistrano
110
+
111
+ If using Rails, a rake task is included and would be best utilized within your deploy script.
112
+
113
+ If not on rails, but using rake, you can easily craft your own rake task. At the very least calling
114
+
115
+ ```ruby
116
+ Release::Notes::Update.new.run
117
+ ```
118
+ is the only instance that needs to be instantiated and invoked.
119
+
120
+ A sample capistrano production file might look something like this:
121
+
122
+ ```ruby
123
+ # config/deploy/production.rb
124
+ server 'the_name_for_my_server', user: 'deploy', roles: %w{app web}
125
+
126
+ set :application, 'my_app'
127
+ set :deploy_to, '/var/www/my_app'
128
+
129
+
130
+ namespace :deploy do
131
+ before :starting, :update_release_notes
132
+
133
+ task :update_release_notes do
134
+ sh 'RAILS_ENV=development bin/rake "update_release_notes:run"'
135
+ # run a second task that generates an auto commit
136
+ # this would be created by you
137
+ sh 'RAILS_ENV=development bin/rake "release_notes:commit"'
138
+ end
139
+ end
140
+ ```
141
+
142
+ Useful information can be found here regarding the
143
+ [capistrano flow](http://capistranorb.com/documentation/getting-started/flow/).
144
+
145
+ As you can see above, it's suggested that after generating your release notes you would
146
+ run an automated commit with a rake task like:
147
+
148
+ ```ruby
149
+ # lib/tasks/commit.rake
150
+ `git commit -am "Release to production #{Time.zone.now}"`
151
+ `git push origin master`
152
+ ```
153
+
154
+ From there, make sure you tag your release after the deploy script runs so that your tag
155
+ includes this last commit.
156
+
157
+ ## Note
158
+
159
+ * Your project must tag releases(release-notes uses the tag date to output the changes)
160
+ (PR's to make this more flexible are welcome)
161
+ * Linking is opinionated and will link to a URI structure of `#{site-url}/#{issue_number}`. It
162
+ will ouput something like: `[HONEYBADGER #33150353](https://app.honeybadger.io/projects/9999/faults/33150353)`.
163
+ This also means that your link_to_labels have to be something like `['HB #']` (PR's to make this more flexible are welcome)
164
+
165
+ ## Development
166
+
167
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
168
+
169
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
170
+
171
+ ## Contributing
172
+
173
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dvmonroe/release-notes. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
174
+
175
+
176
+ ## License
177
+
178
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
179
+
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ Dir.glob('lib/tasks/*.rake').each { |r| import r }
6
+
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'release/notes'
6
+ require 'pry-byebug'
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require 'irb'
16
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ require 'rails/generators/base'
3
+
4
+ module Release
5
+ module Notes
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path('../templates', __FILE__)
9
+
10
+ def create_release_notes_initializer
11
+ copy_file 'release_notes.rb', 'config/initializers/release_notes.rb'
12
+ end
13
+
14
+ def display_readme
15
+ readme 'README'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ *******************************************************************************
2
+
3
+ Next step:
4
+
5
+ 1. Configure release and tag options :
6
+
7
+ # config/initializers/release_notes.rb
8
+ config.by_tag_date = false
9
+
10
+
11
+ *******************************************************************************
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ Release::Notes.configure do |config|
4
+ # config.output_file = './RELEASE_NOTES.md'
5
+ # config.temp_file = './release-notes.tmp.md'
6
+ # config.include_merges = false
7
+ # config.ignore_case = true
8
+ # config.log_format = '- %s'
9
+ # config.extended_regex = true
10
+ # config.bug_labels = %w[Fix Update]
11
+ # config.feature_labels = %w[Add Create]
12
+ # config.misc_labels = %w[Refactor]
13
+ # config.bug_title = '**Fixed bugs:**'
14
+ # config.feature_title = '**Implemented enhancements:**'
15
+ # config.misc_title = '**Miscellaneous:**'
16
+ # config.link_to_labels = %w[]
17
+ # config.link_to_humanize = %w[]
18
+ # config.link_to_sites = %w[]
19
+ # config.timezone = 'America/New_York'
20
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support'
4
+ require 'active_support/core_ext/time'
5
+
6
+ require 'release/notes/date_format'
7
+ require 'release/notes/link'
8
+ require 'release/notes/pretty_print'
9
+
10
+ require 'release/notes/version'
11
+ require 'release/notes/configuration'
12
+ require 'release/notes/git'
13
+ require 'release/notes/system'
14
+ require 'release/notes/with_configuration'
15
+
16
+ require 'release/notes/write'
17
+ require 'release/notes/log'
18
+
19
+ require 'release/notes/railtie' if defined?(Rails)
20
+
21
+ module Release
22
+ module Notes
23
+ class Update
24
+ attr_reader :logger
25
+
26
+ def initialize
27
+ @options = Release::Notes.configuration
28
+ @logger = Release::Notes::Log.new @options
29
+ end
30
+
31
+ def run
32
+ logger.perform
33
+ end
34
+ end
35
+
36
+ def self.root
37
+ File.expand_path('../..', __FILE__)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ class Configuration
6
+ # The absolute path of your generated log.
7
+ # Defaults to `./RELEASE_NOTES.md`.
8
+ # @return [String]
9
+ attr_accessor :output_file
10
+
11
+ # The absolute path of the temporary generated log.
12
+ # Defaults to `./release-notes.tmp.md`.
13
+ # @return [String]
14
+ attr_accessor :temp_file
15
+
16
+ # Determines whether to print commits with more than one parent.
17
+ # Defaults to `false`. For more, see
18
+ # [Git Log Docs](https://git-scm.com/docs/git-log)
19
+ # @return [Boolean]
20
+ attr_accessor :include_merges
21
+
22
+ # Match the regular expression limiting patterns without regard to letter case
23
+ # when printing your git log.
24
+ # Defaults to `true`. For more, see
25
+ # [Git Log Docs](https://git-scm.com/docs/git-log)
26
+ # @return [Boolean]
27
+ attr_accessor :ignore_case
28
+
29
+ # Consider the limiting patterns to be extended regular expressions patterns
30
+ # when printing your git log.
31
+ # Defaults to `true`. For more, see
32
+ # [Git Log Docs](https://git-scm.com/docs/git-log)
33
+ # @return [Boolean]
34
+ attr_accessor :extended_regex
35
+
36
+ # Allows you to specify what information you want to print from your git log
37
+ # Defaults to `%s` for subject. For more, see
38
+ # [Git Log Docs](https://git-scm.com/docs/git-log)
39
+ # @return [String]
40
+ attr_accessor :log_format
41
+
42
+ # Controls the labels grepped for in your commit subjects that will
43
+ # be add under you bug title
44
+ # Defaults to `%w(Fix Update)`.
45
+ # @return [Array]
46
+ attr_accessor :bug_labels
47
+
48
+ # Controls the labels grepped for in your commit subjects that will
49
+ # be add under you feature title
50
+ # Defaults to `%w(Add Create)`.
51
+ # @return [Array]
52
+ attr_accessor :feature_labels
53
+
54
+ # Controls the labels grepped for in your commit subjects that will
55
+ # be add under you miscellaneous title
56
+ # Defaults to `%w(Refactor)`.
57
+ # @return [Array]
58
+ attr_accessor :misc_labels
59
+
60
+ # Controls the title used in your generated log for all bugs listed
61
+ # Defaults to `**Fixed bugs:**`.
62
+ # @return [String]
63
+ attr_accessor :bug_title
64
+
65
+ # Controls the title used in your generated log for all features listed
66
+ # Defaults to `**Implemented enhancements:**`.
67
+ # @return [String]
68
+ attr_accessor :feature_title
69
+
70
+ # Controls the title used in your generated log for all misc commits listed
71
+ # Defaults to `**Miscellaneous:**`.
72
+ # @return [String]
73
+ attr_accessor :misc_title
74
+
75
+ # The labels grepped for in your commit subject that you want to linkify.
76
+ # The index within the array must match the index for the site
77
+ # in `:link_to_humanize` and `:link_to_sites`.
78
+ # Defaults to `[]`.
79
+ # @return [Array]
80
+ attr_accessor :link_to_labels
81
+
82
+ # The humanized output that you'd like to represent the associated `:link_to_label`
83
+ # The index within the array must match the index for the site
84
+ # in `:link_to_label` and `:link_to_sites`.
85
+ # Defaults to `[]`.
86
+ # @return [Array]
87
+ attr_accessor :link_to_humanize
88
+
89
+ # The url for the site that you'd like to represent the associated `:link_to_label`
90
+ # The index within the array must match the index for the site
91
+ # in `:link_to_label` and `:link_to_humanize`.
92
+ # Defaults to `[]`.
93
+ # @return [Array]
94
+ attr_accessor :link_to_sites
95
+
96
+ # Sets the timezone that should be used for setting the date.
97
+ # Defaults to `America/New_York`. For more, see
98
+ # [ActiveSupport Time Zones](http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html)
99
+ # @return [String]
100
+ attr_accessor :timezone
101
+
102
+ # Controls whether your commit subject labels should be removed from the final
103
+ # ouput of your message on the generated log.
104
+ # Defaults to `true`.
105
+ # @return [Boolean]
106
+ attr_accessor :prettify_messages
107
+
108
+ def initialize
109
+ @output_file = './RELEASE_NOTES.md'
110
+ @temp_file = './release-notes.tmp.md'
111
+ @include_merges = false
112
+ @ignore_case = true
113
+ @extended_regex = true
114
+ @log_format = '- %s'
115
+ @bug_labels = %w[Fix Update]
116
+ @feature_labels = %w[Add Create]
117
+ @misc_labels = %w[Refactor]
118
+ @bug_title = '**Fixed bugs:**'
119
+ @feature_title = '**Implemented enhancements:**'
120
+ @misc_title = '**Miscellaneous:**'
121
+ @link_to_labels = %w[]
122
+ @link_to_humanize = %w[]
123
+ @link_to_sites = %w[]
124
+ @timezone = 'America/New_York'
125
+ @prettify_messages = true
126
+ end
127
+
128
+ # @return [String]
129
+ def include_merges?
130
+ @include_merges ? '' : '--no-merges'
131
+ end
132
+
133
+ # @return [String]
134
+ def regex_type
135
+ @extended_regex ? '-E' : ''
136
+ end
137
+
138
+ # @return [String]
139
+ def grep_insensitive?
140
+ @ignore_case ? '-i' : ''
141
+ end
142
+
143
+ # @return [String]
144
+ def bugs
145
+ @bugs ||= generate_regex(@bug_labels)
146
+ end
147
+
148
+ # @return [String]
149
+ def features
150
+ @features ||= generate_regex(@feature_labels)
151
+ end
152
+
153
+ # @return [String]
154
+ def misc
155
+ @misc ||= generate_regex(@misc_labels)
156
+ end
157
+
158
+ # @return [String]
159
+ def all_labels
160
+ @all_labels ||= generate_regex(@bug_labels + @feature_labels + @misc_labels)
161
+ end
162
+
163
+ # @return [Boolean]
164
+ def release_notes_exist?
165
+ File.exist? output_file
166
+ end
167
+
168
+ # @return [Boolean]
169
+ def link_commits?
170
+ link_to_labels.present? && link_to_humanize.present? &&
171
+ link_to_sites.present?
172
+ end
173
+
174
+ # @return [Boolean]
175
+ def prettify_messages?
176
+ @prettify_messages
177
+ end
178
+
179
+ private
180
+
181
+ # @api private
182
+ # Using over Regexp.union
183
+ def generate_regex(arr)
184
+ arr.join('|').insert(0, '(').insert(-1, ')')
185
+ end
186
+ end
187
+
188
+ # @return [Release::Notes::Configuration] Release::Notes's current configuration
189
+ def self.configuration
190
+ @configuration ||= Configuration.new
191
+ end
192
+
193
+ # Set Release::Notes's configuration
194
+ # @param config [Release::Notes::Configuration]
195
+ def self.configuration=(config)
196
+ @configuration = config
197
+ end
198
+
199
+ # Modify Release::Notes's current configuration
200
+ # @yieldparam [Release::Notes] config current Release::Notes config
201
+ # ```
202
+ # Release::Notes.configure do |config|
203
+ # config.routes = false
204
+ # end
205
+ # ```
206
+ def self.configure
207
+ yield configuration
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ class DateFormat
6
+ attr_reader :config, :time_now
7
+ delegate :timezone, to: :config
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ Time.zone = timezone
12
+
13
+ @time_now = Time.zone.now
14
+ end
15
+
16
+ def date_humanized(date: nil)
17
+ date = date.present? ? Time.zone.parse(date) : time_now
18
+ date.strftime('%B %d, %Y %r %Z')
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ module Git
6
+ module_function
7
+
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ delegate :log_format, :grep_insensitive?, :regex_type, :include_merges?, to: :config
12
+
13
+ def log(**opts)
14
+ "git log '#{opts[:tag_from]}'..'#{opts[:tag_to]}' --grep='#{opts[:label]}'" \
15
+ " #{regex_type} #{grep_insensitive?}" \
16
+ " #{include_merges?} --format='#{log_format}'"
17
+ end
18
+ end
19
+
20
+ def last_tag
21
+ 'git describe --abbrev=0 --tags'
22
+ end
23
+
24
+ def tag_date(tag)
25
+ "git log -1 --format=%ai #{tag}"
26
+ end
27
+
28
+ def read_all_tags
29
+ 'git tags | sort -u -r'
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ module Link
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :link_to_labels, :link_to_sites, :link_to_humanize, to: :config
10
+
11
+ def link_lines(lines:)
12
+ new_lines = ''
13
+ lines.split("\n").each do |line|
14
+ unless link_to_labels.any? { |la| line.include? la }
15
+ new_lines += "#{line}\n"
16
+ next
17
+ end
18
+ link_to_labels.each_with_index do |label, i|
19
+ next unless line.include? label
20
+ words = line.split(/\s/)
21
+ words.each do |word|
22
+ next unless (word =~ /^#.*/)&.zero?
23
+ new_lines += "#{replace(line, word, label, i)}\n"
24
+ end
25
+ end
26
+ end
27
+ new_lines
28
+ end
29
+
30
+ private
31
+
32
+ # @api private
33
+ def replace(line, issue_number, label, index)
34
+ identifier = "#{label.split(/\s/)[0]} #{issue_number}"
35
+ humanized = "#{link_to_humanize[index]} #{issue_number}"
36
+ linked = "[#{humanized}](#{link_to_sites[index]})"
37
+
38
+ line.gsub! identifier, linked
39
+ line
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ class Log
6
+ include System
7
+ include WithConfiguration
8
+
9
+ attr_reader :config, :writer, :date_formatter
10
+ attr_reader :all_tags
11
+
12
+ delegate :all_labels, :features, :bugs, :misc, :feature_title,
13
+ :bug_title, :misc_title, :release_notes_exist?, to: :config
14
+
15
+ delegate :date_humanized, :format_tag_date, to: :date_formatter
16
+ delegate :digest, to: :writer
17
+
18
+ def initialize(config)
19
+ @config = config
20
+
21
+ setup_writer
22
+ setup_date_formatter
23
+ end
24
+
25
+ def perform
26
+ if release_notes_exist?
27
+ # Find the last tag and group all commits
28
+ # under a date header at the time this is run
29
+ find_last_tag_and_log
30
+ else
31
+ # Find all tags and get the logs between each tag
32
+ # run this the first time if nothing exists
33
+ find_all_tags_and_log_all
34
+ end
35
+ writer.write_new_file
36
+ end
37
+
38
+ private
39
+
40
+ def all_tags
41
+ @all_tags ||= System.all_tags.split("\n")
42
+ # return Error.new(msg: :missing_tags) unless all_tags.present?
43
+ end
44
+
45
+ # @api private
46
+ def copy_single_tag_of_activity(tag_from:, tag_to: nil)
47
+ tag_to ||= 'HEAD'
48
+ [features, bugs, misc].each_with_index do |regex, i|
49
+ log = system_call(tag_from: tag_from, tag_to: tag_to, label: regex)
50
+ digest(date: nil, title: titles[i], log_message: log) if log.present?
51
+ end
52
+ end
53
+
54
+ # @api private
55
+ def find_last_tag_and_log
56
+ last_tag = system_last_tag.delete!("\n")
57
+ return false unless system_call(tag_from: last_tag, label: all_labels).present?
58
+
59
+ # output the date right now
60
+ digest date: date_humanized
61
+ copy_single_tag_of_activity(tag_from: last_tag)
62
+ end
63
+
64
+ # @api private
65
+ def setup_writer
66
+ @writer = Release::Notes::Write.new config
67
+ end
68
+
69
+ # @api private
70
+ def setup_date_formatter
71
+ @date_formatter = Release::Notes::DateFormat.new config
72
+ end
73
+
74
+ # @api private
75
+ def find_all_tags_and_log_all
76
+ all_tags.each_with_index do |ta, i|
77
+ previous_tag = all_tags[i + 1]
78
+ next unless previous_tag.present? &&
79
+ system_call(tag_from: previous_tag, tag_to: ta, label: all_labels).present?
80
+
81
+ digest date: date_humanized(date: System.tag_date(tag: ta))
82
+ copy_single_tag_of_activity(tag_from: previous_tag, tag_to: ta)
83
+ end
84
+ end
85
+
86
+ # @api private
87
+ def system_call(**opts)
88
+ with_config(config: config) do
89
+ system_log(opts)
90
+ end
91
+ end
92
+
93
+ # @api private
94
+ def titles
95
+ [feature_title, bug_title, misc_title]
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ module PrettyPrint
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ delegate :all_labels, to: :config
10
+
11
+ def prettify(line:)
12
+ line.gsub(labels_regex, '').strip
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ # @api private
19
+ def labels_regex
20
+ Regexp.new all_labels, Regexp::IGNORECASE
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load 'tasks/update_release_notes.rake'
8
+ end
9
+
10
+ generators do
11
+ require 'generators/release/notes/install/install_generator.rb'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ module System
6
+ module_function
7
+
8
+ extend ActiveSupport::Concern
9
+ include Git
10
+
11
+ included do
12
+ def system_log(**opts)
13
+ `#{log(opts)}`
14
+ end
15
+ end
16
+
17
+ def all_tags
18
+ `#{Git.read_all_tags}`
19
+ end
20
+
21
+ def system_last_tag
22
+ `#{Git.last_tag}`
23
+ end
24
+
25
+ def tag_date(tag: nil)
26
+ tag ||= system_last_tag
27
+ `#{Git.tag_date(tag)}`
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ module WithConfiguration
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_reader :config, :date_formatter
10
+
11
+ def with_config(**opts, &_block)
12
+ @confg = opts.fetch(:config, {})
13
+ yield
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Release
4
+ module Notes
5
+ class Write
6
+ include Link
7
+ include PrettyPrint
8
+ include WithConfiguration
9
+
10
+ attr_accessor :config
11
+
12
+ delegate :output_file, :temp_file, :link_commits?, :all_labels,
13
+ :prettify_messages?, :release_notes_exist?, to: :config
14
+
15
+ def initialize(config)
16
+ @config = config
17
+ # create a new temp file regardless if it exists
18
+ new_temp_file_template
19
+ end
20
+
21
+ def digest(date: nil, title: nil, log_message: nil)
22
+ File.open(temp_file, 'a') do |fi|
23
+ fi << "\n\n## #{date}\n" if date
24
+ fi << "\n#{title}\n\n" if title && !date
25
+ fi << "#{title}\n\n" if title && date
26
+
27
+ break unless log_message
28
+ # link messages if needed
29
+ msg = link_message log_message
30
+ # remove tags if needed
31
+ msg = with_config(config: config) { prettify(line: msg) } if prettify_messages?
32
+ fi << "#{msg}\n"
33
+ end
34
+ end
35
+
36
+ # append old file to new temp file
37
+ # overwrite output file with tmp file
38
+ def write_new_file
39
+ copy_over_notes if release_notes_exist?
40
+
41
+ FileUtils.cp(temp_file, output_file)
42
+ FileUtils.rm temp_file
43
+ end
44
+
45
+ private
46
+
47
+ # @api private
48
+ def copy_over_notes
49
+ File.open(temp_file, 'a') do |f|
50
+ f << "\n"
51
+ IO.readlines(output_file)[2..-1].each { |line| f << line }
52
+ end
53
+ end
54
+
55
+ # @api private
56
+ def link_message(log_message)
57
+ return log_message unless link_commits?
58
+ with_config(config: config) do
59
+ link_lines(lines: log_message)
60
+ end
61
+ end
62
+
63
+ # @api private
64
+ def new_temp_file_template
65
+ File.new(temp_file, 'w')
66
+ File.open(temp_file, 'a') do |fi|
67
+ fi << "# Release Notes\n----------------------"
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :update_release_notes do
4
+ task run: :environment do
5
+ Release::Notes::Update.new.run
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'release/notes/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "release-notes"
8
+ spec.version = Release::Notes::VERSION
9
+ spec.authors = ["Drew Monroe"]
10
+ spec.email = ["dvmonroe6@gmail.com"]
11
+
12
+ spec.summary = "Release notes for stakeholders"
13
+ spec.description = spec.summary
14
+ spec.homepage = "http://github.com/dvmonroe/release-notes"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'activesupport'
23
+ spec.add_development_dependency 'bundler', '~> 1.12'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: release-notes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Drew Monroe
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-05-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.12'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ description: Release notes for stakeholders
56
+ email:
57
+ - dvmonroe6@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".rspec"
64
+ - ".ruby-version"
65
+ - ".travis.yml"
66
+ - ".yardopts"
67
+ - CODE_OF_CONDUCT.md
68
+ - Gemfile
69
+ - LICENSE.txt
70
+ - README.md
71
+ - Rakefile
72
+ - bin/console
73
+ - bin/setup
74
+ - lib/generators/release/notes/install/install_generator.rb
75
+ - lib/generators/release/notes/install/templates/README
76
+ - lib/generators/release/notes/install/templates/release_notes.rb
77
+ - lib/release/notes.rb
78
+ - lib/release/notes/configuration.rb
79
+ - lib/release/notes/date_format.rb
80
+ - lib/release/notes/git.rb
81
+ - lib/release/notes/link.rb
82
+ - lib/release/notes/log.rb
83
+ - lib/release/notes/pretty_print.rb
84
+ - lib/release/notes/railtie.rb
85
+ - lib/release/notes/system.rb
86
+ - lib/release/notes/version.rb
87
+ - lib/release/notes/with_configuration.rb
88
+ - lib/release/notes/write.rb
89
+ - lib/tasks/update_release_notes.rake
90
+ - release-notes.gemspec
91
+ homepage: http://github.com/dvmonroe/release-notes
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 2.6.10
112
+ signing_key:
113
+ specification_version: 4
114
+ summary: Release notes for stakeholders
115
+ test_files: []