changelog-notifier 1.5.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 374a3a79445b96144828a772b1cf324709a2cc69299facf1d29c4b1811ab0a37
4
+ data.tar.gz: c419bfa4d08532dc332789372018c7f6d1ce70b92e7d07289f4a74907a595cc6
5
+ SHA512:
6
+ metadata.gz: f3a42bfdea37eef6b474c803ad6761426171ee44d471d00af49943be9b104ccc4a6a3af762e0670acfab2614011915591c8d5696fc754c9d6d7d542d6365fd24
7
+ data.tar.gz: 2c40c703e172c8bb94667818924d434a4758be09d6117e7714856ef4df3ccefcb0732066565cf8bc74114b37abeeccb27e9b0db74c10be5e34e1e967f0abf4a4
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ spec/tmp
14
+
15
+ *.gem
@@ -0,0 +1,30 @@
1
+ # This file is a template, and might need editing before it works on your project.
2
+ docker-build-master:
3
+ # Official docker image.
4
+ image: docker:latest
5
+ stage: build
6
+ services:
7
+ - docker:dind
8
+ before_script:
9
+ - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
10
+ script:
11
+ - docker build --pull -t "$CI_REGISTRY_IMAGE" .
12
+ - docker run --rm "$CI_REGISTRY_IMAGE"
13
+ - docker push "$CI_REGISTRY_IMAGE"
14
+ only:
15
+ - master
16
+
17
+ docker-build:
18
+ # Official docker image.
19
+ image: docker:latest
20
+ stage: build
21
+ services:
22
+ - docker:dind
23
+ before_script:
24
+ - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
25
+ script:
26
+ - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" .
27
+ - docker run --rm "$CI_REGISTRY_IMAGE"
28
+ - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
29
+ except:
30
+ - master
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,4 @@
1
+ require: rubocop-rspec
2
+
3
+ Naming/RescuedExceptionsVariableName:
4
+ PreferredName: error
@@ -0,0 +1,49 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+
10
+
11
+ ## [1.5.0] - 2020-10-18
12
+ ### Added
13
+ - ActiveRecord adapter
14
+
15
+ ## [1.4.0] - 2020-10-13
16
+ ### Added
17
+ - Screenshot in the `README.md` file
18
+
19
+ ### Removed
20
+ - Remaning debugs
21
+
22
+ ## [1.3.0] - 2020-10-13
23
+ ### Fixed
24
+ - Running commands remotely
25
+
26
+ ## [1.2.0] - 2020-10-13
27
+ ### Fixed
28
+ - Using git on a Capistrano server
29
+
30
+ ## [1.1.0] - 2020-10-13
31
+ ### Added
32
+ - Improves application name formatting in Slack adapter
33
+
34
+ ## [1.0.0] - 2020-10-12
35
+ ### Added
36
+ - Capistrano entrypoint
37
+ - CHANGELOG.md parser based on http://keepachangelog.com
38
+ - Version fetching based on git tags
39
+ - Version commit author fetching
40
+ - Slack adapter to post the changelog to a Slack channel
41
+ - Slack formatter
42
+
43
+ [Unreleased]: https://gitlab.com/zedtux/changelog-notifier/-/compare/v1.5.0...master
44
+ [1.5.0]: https://gitlab.com/zedtux/changelog-notifier/-/compare/v1.4.0...v1.5.0
45
+ [1.4.0]: https://gitlab.com/zedtux/changelog-notifier/-/compare/v1.3.0...v1.4.0
46
+ [1.3.0]: https://gitlab.com/zedtux/changelog-notifier/-/compare/v1.2.0...v1.3.0
47
+ [1.2.0]: https://gitlab.com/zedtux/changelog-notifier/-/compare/v1.1.0...v1.2.0
48
+ [1.1.0]: https://gitlab.com/zedtux/changelog-notifier/-/compare/v1.0.0...v1.1.0
49
+ [1.0.0]: https://gitlab.com/zedtux/changelog-notifier/-/tags/v1.0.0
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at zedtux@zedroot.org. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
@@ -0,0 +1,60 @@
1
+ #
2
+ # Builds a temporary image, with all the required dependencies used to compile
3
+ # the dependencies' dependencies.
4
+ # This image will be destroyed at the end of the build command.
5
+ #
6
+ FROM ruby:2.7-alpine3.12 AS build-env
7
+
8
+ ARG GEM_ROOT=/gem
9
+ ARG BUILD_PACKAGES="build-base git"
10
+ ARG DEV_PACKAGES="sqlite-dev"
11
+ ARG RUBY_PACKAGES="tzdata"
12
+
13
+ RUN apk update && \
14
+ apk upgrade && \
15
+ apk add --update --no-cache $BUILD_PACKAGES \
16
+ $DEV_PACKAGES \
17
+ $RUBY_PACKAGES && \
18
+ mkdir -p /gem/
19
+
20
+ WORKDIR $GEM_ROOT
21
+
22
+ COPY Gemfile* *.gemspec /gem/
23
+ COPY lib/changelog/notifier/version.rb /gem/lib/changelog/notifier/version.rb
24
+
25
+ RUN touch ~/.gemrc && \
26
+ echo "gem: --no-ri --no-rdoc" >> ~/.gemrc && \
27
+ gem install rubygems-update && \
28
+ update_rubygems && \
29
+ gem install bundler && \
30
+ bundle install --jobs $(nproc) && \
31
+ rm -rf /usr/local/bundle/cache/*.gem && \
32
+ find /usr/local/bundle/gems/ -name "*.c" -delete && \
33
+ find /usr/local/bundle/gems/ -name "*.o" -delete
34
+
35
+ COPY . /gem/
36
+
37
+ #
38
+ # Builds the final image with the minimum of system packages
39
+ # and copy the gem's sources, Bundler gems and Yarn packages.
40
+ #
41
+
42
+ FROM ruby:2.7-alpine3.12
43
+
44
+ LABEL maintainer="zedtux"
45
+
46
+ ARG GEM_ROOT=/gem
47
+ ARG PACKAGES="git sqlite sqlite-libs tzdata"
48
+
49
+ RUN apk update && \
50
+ apk upgrade && \
51
+ apk add --update --no-cache $PACKAGES && \
52
+ mkdir -p /gem/
53
+
54
+ WORKDIR $GEM_ROOT
55
+
56
+ COPY --from=build-env /usr/local/bundle/ /usr/local/bundle/
57
+ COPY --from=build-env $GEM_ROOT $GEM_ROOT
58
+
59
+ ENTRYPOINT ["bundle", "exec"]
60
+ CMD ["rake"]
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in changelog-notifier.gemspec
4
+ gemspec
5
+
6
+ gem 'activerecord', '~> 6.0'
7
+ gem 'database_cleaner-active_record', '~> 1.8'
8
+ gem 'rake', '~> 12.0'
9
+ gem 'rspec', '~> 3.0'
10
+ gem 'slack-notifier', '~> 2'
11
+ gem 'sqlite3', '~> 1.4'
12
+ gem 'webmock', '~> 3.9'
@@ -0,0 +1,75 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ changelog-notifier (1.4.0)
5
+ titleize (~> 1.4.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activemodel (6.0.3.4)
11
+ activesupport (= 6.0.3.4)
12
+ activerecord (6.0.3.4)
13
+ activemodel (= 6.0.3.4)
14
+ activesupport (= 6.0.3.4)
15
+ activesupport (6.0.3.4)
16
+ concurrent-ruby (~> 1.0, >= 1.0.2)
17
+ i18n (>= 0.7, < 2)
18
+ minitest (~> 5.1)
19
+ tzinfo (~> 1.1)
20
+ zeitwerk (~> 2.2, >= 2.2.2)
21
+ addressable (2.7.0)
22
+ public_suffix (>= 2.0.2, < 5.0)
23
+ concurrent-ruby (1.1.7)
24
+ crack (0.4.4)
25
+ database_cleaner (1.8.5)
26
+ database_cleaner-active_record (1.8.0)
27
+ activerecord
28
+ database_cleaner (~> 1.8.0)
29
+ diff-lcs (1.4.4)
30
+ hashdiff (1.0.1)
31
+ i18n (1.8.5)
32
+ concurrent-ruby (~> 1.0)
33
+ minitest (5.14.2)
34
+ public_suffix (4.0.6)
35
+ rake (12.3.3)
36
+ rspec (3.9.0)
37
+ rspec-core (~> 3.9.0)
38
+ rspec-expectations (~> 3.9.0)
39
+ rspec-mocks (~> 3.9.0)
40
+ rspec-core (3.9.3)
41
+ rspec-support (~> 3.9.3)
42
+ rspec-expectations (3.9.2)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.9.0)
45
+ rspec-mocks (3.9.1)
46
+ diff-lcs (>= 1.2.0, < 2.0)
47
+ rspec-support (~> 3.9.0)
48
+ rspec-support (3.9.3)
49
+ slack-notifier (2.3.2)
50
+ sqlite3 (1.4.2)
51
+ thread_safe (0.3.6)
52
+ titleize (1.4.1)
53
+ tzinfo (1.2.7)
54
+ thread_safe (~> 0.1)
55
+ webmock (3.9.1)
56
+ addressable (>= 2.3.6)
57
+ crack (>= 0.3.2)
58
+ hashdiff (>= 0.4.0, < 2.0.0)
59
+ zeitwerk (2.4.0)
60
+
61
+ PLATFORMS
62
+ ruby
63
+
64
+ DEPENDENCIES
65
+ activerecord (~> 6.0)
66
+ changelog-notifier!
67
+ database_cleaner-active_record (~> 1.8)
68
+ rake (~> 12.0)
69
+ rspec (~> 3.0)
70
+ slack-notifier (~> 2)
71
+ sqlite3 (~> 1.4)
72
+ webmock (~> 3.9)
73
+
74
+ BUNDLED WITH
75
+ 2.1.4
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Guillaume Hain
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.
@@ -0,0 +1,152 @@
1
+ # changelog-notifier
2
+
3
+ This gem reads [your `CHANGELOG.md` file](https://keepachangelog.com) and extract the release note for your project's version (fetched from your git tags) and posts it to a Slack channel when deploying your app with Capistrano.
4
+
5
+ This allows you to keep informed your team about releases deployments.
6
+
7
+ To summarize, it runs your `CHANGELOG.md` file into this:
8
+
9
+ ![](screenshots/changelog-notifiier_slack.png)
10
+
11
+ ## Concepts
12
+
13
+ ### Entrypoints
14
+
15
+ [Entrypoints](https://gitlab.com/zedtux/changelog-notifier/-/tree/master/lib/changelog/notifier/entrypoints) are the inputs, or the triggers. As of now, only the Capistrano entrypoint exists, which means you can only publish your `CHANGELOG.md` *FROM* a Capistrano deployment.
16
+
17
+ You can add any kind of entrypoints like a cron one, a bot and more!
18
+
19
+ ### Parsers
20
+
21
+ [Parsers](https://gitlab.com/zedtux/changelog-notifier/-/tree/master/lib/changelog/notifier/parsers) are the heart of this gem, and are responsible to read and transform a `CHANGELOG.md` file into a standardized `Hash`.
22
+
23
+ In other words they are transforming something like that:
24
+
25
+ ```
26
+ ## [1.1.0] - 2020-10-13
27
+ ### Added
28
+ - Improves application name formatting in Slack adapter
29
+
30
+ ### Removed
31
+ - All the bad code
32
+ ```
33
+
34
+ Into that:
35
+
36
+ ```ruby
37
+ {
38
+ version: '1.1.0',
39
+ date: '2020-10-13',
40
+ url: 'https://github.com/olivierlacan/keep-a-changelog/compare/v1.0.0...v1.1.0',
41
+ changes: {
42
+ added: [
43
+ 'Improves application name formatting in Slack adapter',
44
+ ],
45
+ removed: [
46
+ 'All the bad code'
47
+ ]
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### Adapters
53
+
54
+ [Adapters](https://gitlab.com/zedtux/changelog-notifier/-/tree/master/lib/changelog/notifier/adapters) are the output. Adapters are using [formatters](https://gitlab.com/zedtux/changelog-notifier/-/tree/master/lib/changelog/notifier/formatters) in order to transform the parsed `CHANGELOG.md` into something that can be used by the adapters.
55
+
56
+ As of now, there are 2 supported adapters:
57
+
58
+ * the Slack adapter which allows you to publish your `CHANGELOG.md` *TO* a Slack channel
59
+ * the ActiveRecord which allows you to create an ActiveRecord into your database
60
+
61
+ You can use many adapters at the same time so if you'd like to post to Slack *AND* to create an entry in your database, configures both and you'll get both!
62
+
63
+ You can add adapters for anythings like Twitter, Mails, SMS, WhatsApp and more!
64
+
65
+ ## Installation
66
+
67
+ Add this line to your application's Gemfile:
68
+
69
+ ```ruby
70
+ gem 'changelog-notifier', '~> 1.4'
71
+ gem 'slack-notifier', '~> 2' # For Slack notifications
72
+ ```
73
+
74
+ And then execute:
75
+
76
+ $ bundle
77
+
78
+ Or install it yourself as:
79
+
80
+ $ gem install changelog-notifier
81
+
82
+ ## Usage
83
+
84
+ ### First
85
+
86
+ As of now there's only one [Entrypoint](#entrypoints) which is Capistrano, so you have to import the gem's Capistrano entrypoint:
87
+
88
+ 1. Add the following `require` to your `Capfile`
89
+ ```ruby
90
+ require 'changelog/notifier/capistrano'
91
+ ```
92
+ 2. Add and configure the options in your `config/deploy.rb` file:
93
+ ```ruby
94
+ # Where should changelog-notifier run?
95
+ set :changelog_notifier_role, :app
96
+ ```
97
+
98
+ ### Using the Slack adapter
99
+
100
+ 3. Configure the Slack adapter:
101
+ ```ruby
102
+ # Slack options
103
+ #
104
+ # Your webhook URL
105
+ set :changelog_notifier_slack_webhook_url, 'https://hooks.slack.com/services/...'
106
+ # The Slack channel where to post the release note
107
+ set :changelog_notifier_slack_channel, '#general'
108
+ # The Icon Emoji to be used when posting the release note
109
+ set :changelog_notifier_slack_icon_emoji, ':package:'
110
+ ```
111
+
112
+ ### Using the ActiveRecord adapter
113
+
114
+ 3. Configure the ActiveRecord adapter:
115
+ ```ruby
116
+ # ActiveRecord options
117
+ #
118
+ # Your ActiveRecord model name
119
+ set :changelog_notifier_active_record_model, Release
120
+ # The column/field from your ActiveRecord model where to set the version
121
+ set :changelog_notifier_active_record_version_field, 'version'
122
+ # The column/field from your ActiveRecord model where to store the release note
123
+ set :changelog_notifier_active_record_release_node_field, 'release_note'
124
+ # The columns/fields from your ActiveRecord model that you need to set (optional)
125
+ set :changelog_notifier_active_record_other_fields, { field1: 'ok', field2: true }
126
+ ```
127
+
128
+ ### Finally
129
+
130
+ 3. Maintain your `CHANGELOG.md` file as described at https://keepachangelog.com
131
+ 4. Tag with your version
132
+ 5. Deploy with Capistrano
133
+
134
+ When you will deploy a tagged commit, and a matching version exists in your `CHANGELOG.md` file, a Slack post will be sent in the case the Capistrano deployment suceeded.
135
+
136
+ ## Development
137
+
138
+ After checking out the repo, and installed [Docker](https://docs.docker.com/get-docker/), run `docker build -t $(whoami)/changelog-notifier .` to build the development Docker image. Then, run `docker run --rm --volume "$PWD":/gem $(whoami)/changelog-notifier` to run the tests. You can also run `docker run --rm -it --volume "$PWD":/gem $(whoami)/changelog-notifier bin/console` for an interactive prompt that will allow you to experiment.
139
+
140
+ To release a new version, update the version number in `version.rb`, and then run `docker run --rm --volume "$PWD":/gem $(whoami)/changelog-notifier 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).
141
+
142
+ ## Contributing
143
+
144
+ Bug reports and merge requests are welcome on Gitlab at https://gitlab.com/zedtux/changelog-notifier. 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.
145
+
146
+ ## License
147
+
148
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
149
+
150
+ ## Code of Conduct
151
+
152
+ Everyone interacting in the Changelog::Notifier project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://gitlab.com/zedtux/changelog-notifier/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'changelog/notifier'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
@@ -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,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/changelog/notifier/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'changelog-notifier'
7
+ spec.version = Changelog::Notifier::VERSION
8
+ spec.authors = ['Guillaume Hain']
9
+ spec.email = ['zedtux@zedroot.org']
10
+
11
+ spec.summary = "Sends current version's release note from your " \
12
+ 'CHANGELOG.md file'
13
+ spec.description = 'This gem sends the release note, extracted from your ' \
14
+ 'CHANGELOG.md file, to Slack when deployin with ' \
15
+ 'Capistrano'
16
+ spec.homepage = 'https://gitlab.com/zedtux/changelog-notifier'
17
+ spec.license = 'MIT'
18
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
19
+
20
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
21
+ spec.metadata['homepage_uri'] = spec.homepage
22
+ spec.metadata['source_code_uri'] = 'https://gitlab.com/zedtux/changelog-notifier'
23
+ spec.metadata['changelog_uri'] = 'https://gitlab.com/zedtux/changelog-notifier/-/blob/master/CHANGELOG.md'
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
28
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ spec.add_dependency 'titleize', '~> 1.4.1'
35
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'changelog/notifier/errors'
4
+ require 'changelog/notifier/adapters/base'
5
+ require 'changelog/notifier/adapters/active_record'
6
+ require 'changelog/notifier/adapters/slack'
7
+ require 'changelog/notifier/formatters/active_record'
8
+ require 'changelog/notifier/formatters/slack'
9
+ require 'changelog/notifier/git_commit_author_fetcher'
10
+ require 'changelog/notifier/git_tag_fetcher'
11
+ require 'changelog/notifier/parsers/markdown'
12
+ require 'changelog/notifier/version'
13
+
14
+ module Changelog
15
+ module Notifier
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Changelog
4
+ module Notifier
5
+ module Adapters
6
+ #
7
+ # Creates an ActiveRecord with the changelog
8
+ #
9
+ class ActiveRecord < Base
10
+ def publish!(release_note_hash, version)
11
+ formatted_release_note = format_release_note_hash(release_note_hash)
12
+
13
+ create_attributes = {}
14
+ create_attributes[version_field] = version
15
+ create_attributes[release_node_field] = formatted_release_note
16
+
17
+ Array(@other_fields).each do |field, value|
18
+ create_attributes[field] = value
19
+ end
20
+
21
+ @model.create!(create_attributes)
22
+ end
23
+
24
+ private
25
+
26
+ #
27
+ # Fetches all the configuration variables from Capistrano.
28
+ #
29
+ def fetches_adapter_configuration
30
+ @model = @capistrano.fetch(:changelog_notifier_active_record_model)
31
+ @version_field = @capistrano.fetch(
32
+ :changelog_notifier_active_record_version_field
33
+ )
34
+ @release_node_field = @capistrano.fetch(
35
+ :changelog_notifier_active_record_release_node_field
36
+ )
37
+ @other_fields = @capistrano.fetch(
38
+ :changelog_notifier_active_record_other_fields
39
+ )
40
+ end
41
+
42
+ def format_release_note_hash(release_note_hash)
43
+ Changelog::Notifier::Formatters::ActiveRecord.new(release_note_hash)
44
+ .format
45
+ end
46
+
47
+ def release_node_field
48
+ @release_node_field || 'release_node'
49
+ end
50
+
51
+ def version_field
52
+ @version_field || 'version'
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Changelog
4
+ module Notifier
5
+ module Adapters
6
+ class Base
7
+ def initialize(capistrano)
8
+ @capistrano = capistrano
9
+
10
+ unless @capistrano
11
+ raise ArgumentError, 'No Capistrano instance passed while it is ' \
12
+ 'required.'
13
+ end
14
+
15
+ fetches_adapter_configuration
16
+ end
17
+
18
+ def publish!(release_note_hash, version)
19
+ raise NotImplementedError
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'slack-notifier'
4
+
5
+ module Changelog
6
+ module Notifier
7
+ module Adapters
8
+ #
9
+ # Slack adapter sends the release note in a Slack channel.
10
+ #
11
+ class Slack < Base
12
+ def publish!(release_note_hash, version)
13
+ formatted_release_note = format_release_note_hash(release_note_hash)
14
+
15
+ notifier = instantiate_slack_notifier
16
+
17
+ @capistrano.info "Posting #{version}'s release note in the " \
18
+ "Slack #{channel} channel using the #{icon_emoji} " \
19
+ 'emoji icon ...'
20
+
21
+ notifier.post formatted_release_note
22
+ end
23
+
24
+ private
25
+
26
+ def channel
27
+ @channel || '#general'
28
+ end
29
+
30
+ #
31
+ # Fetches all the configuration variables from Capistrano.
32
+ #
33
+ def fetches_adapter_configuration
34
+ @webhook_url = @capistrano.fetch(
35
+ :changelog_notifier_slack_webhook_url
36
+ )
37
+ @channel = @capistrano.fetch(:changelog_notifier_slack_channel)
38
+ @icon_emoji = @capistrano.fetch(:changelog_notifier_slack_icon_emoji)
39
+ end
40
+
41
+ def format_release_note_hash(release_note_hash)
42
+ Changelog::Notifier::Formatters::Slack.new(release_note_hash).format
43
+ end
44
+
45
+ def icon_emoji
46
+ @icon_emoji || ':package:'
47
+ end
48
+
49
+ def instantiate_slack_notifier
50
+ ::Slack::Notifier.new(
51
+ @webhook_url,
52
+ channel: channel,
53
+ icon_emoji: icon_emoji,
54
+ username: 'ChangeLog Notifier'
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'changelog/notifier/entrypoints/capistrano'
4
+ require 'changelog/notifier/git_tag_fetcher'
5
+ require 'changelog/notifier/git_commit_author_fetcher'
6
+ require 'changelog/notifier/errors'
7
+ require 'changelog/notifier/parsers/markdown'
8
+ require 'changelog/notifier/adapters/slack'
9
+ require 'changelog/notifier/formatters/slack'
10
+
11
+ load File.expand_path('capistrano/tasks/changelog_notifier.rake', __dir__)
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :load do
4
+ task :defaults do
5
+ set :changelog_notifier_role, :app
6
+
7
+ # Slack
8
+ set :changelog_notifier_slack_webhook_url, nil
9
+ set :changelog_notifier_slack_channel, '#general'
10
+ set :changelog_notifier_slack_icon_emoji, ':package:'
11
+
12
+ # ActiveRecord
13
+ set :changelog_notifier_active_record_model, nil # nil means disabled
14
+ set :changelog_notifier_active_record_version_field, 'version'
15
+ set :changelog_notifier_active_record_release_node_field, 'release_note'
16
+ set :changelog_notifier_active_record_other_fields, {}
17
+ end
18
+ end
19
+
20
+ namespace :deploy do
21
+ after :deploy, 'changelog_notifier:post_release_note'
22
+ end
23
+
24
+ namespace :changelog_notifier do
25
+ desc "Posts version's release notee do Slack"
26
+ task :post_release_note do
27
+ on roles(fetch(:changelog_notifier_role)) do
28
+ within current_path do
29
+ Changelog::Notifier::Entrypoints::Capistrano.run!(self)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Changelog
4
+ module Notifier
5
+ module Entrypoints
6
+ #
7
+ # Tries to publish the version's release note after a successful
8
+ # Capistrano deployment.
9
+ #
10
+ class Capistrano
11
+ def initialize(instance)
12
+ @capistrano = instance
13
+ end
14
+
15
+ def publish_release_note
16
+ ensure_release_has_changelog_file &&
17
+ fetch_version_from_git_tags_and_commit_author &&
18
+ parse_changelog_file &&
19
+ run_through_adapters
20
+ end
21
+
22
+ def self.run!(instance)
23
+ entrypoint = Changelog::Notifier::Entrypoints::Capistrano.new(instance)
24
+ entrypoint.publish_release_note
25
+ end
26
+
27
+ private
28
+
29
+ def ensure_release_has_changelog_file
30
+ return true if File.file?('CHANGELOG.md')
31
+
32
+ @capistrano.warn 'You repository has no CHANGELOG.md file. ' \
33
+ 'Skipping sending release note.'
34
+
35
+ false
36
+ end
37
+
38
+ def fetch_version_from_git_tags_and_commit_author
39
+ @git_repo_path = @capistrano.fetch(:repo_path)
40
+
41
+ @capistrano.info "Git dir is now #{@git_repo_path}."
42
+
43
+ fetch_version_from_git_tags && fetch_commit_author
44
+ end
45
+
46
+ def fetch_version_from_git_tags
47
+ # Retrives the current's commit version tag if any
48
+ @version = Changelog::Notifier::GitTagFetcher.fetch(
49
+ capistrano: @capistrano,
50
+ path: @git_repo_path
51
+ )
52
+
53
+ return true if @version
54
+
55
+ @capistrano.warn 'No version tag found from the currernt commit. ' \
56
+ 'Skipping sending release note.'
57
+
58
+ false
59
+ end
60
+
61
+ def fetch_commit_author
62
+ # Retrives the current's commit author
63
+ @author = Changelog::Notifier::GitCommitAuthorFetcher.fetch(
64
+ capistrano: @capistrano,
65
+ path: @git_repo_path
66
+ )
67
+
68
+ return true if @author
69
+
70
+ @capistrano.warn 'Commit author cannot be found from the currernt ' \
71
+ ' commit. Skipping sending release note.'
72
+
73
+ false
74
+ end
75
+
76
+ def parse_changelog_file
77
+ parser = Changelog::Notifier::Parsers::Markdown.new(
78
+ File.read('CHANGELOG.md')
79
+ )
80
+
81
+ @release_note_hash = parser.extract(@version)
82
+
83
+ @release_note_hash[:author] = @author
84
+ @release_note_hash[:application] = @capistrano.fetch(
85
+ :application,
86
+ 'the application'
87
+ )
88
+
89
+ true
90
+ rescue ArgumentError => error
91
+ @capistrano.error "Parsing CHANGELOG.md failed: #{error.message}. " \
92
+ 'Skipping sending release note.'
93
+ rescue Changelog::Notifier::ReleaseNoteNotFound
94
+ @capistrano.error 'Parsing CHANGELOG.md failed: Missing release ' \
95
+ "note for version #{@version}." \
96
+ 'Skipping sending release note.'
97
+ end
98
+
99
+ def run_through_adapters
100
+ [
101
+ Changelog::Notifier::Adapters::ActiveRecord,
102
+ Changelog::Notifier::Adapters::Slack
103
+ ].each do |adapter|
104
+ adapter.new(@capistrano).publish!(@release_note_hash, @version)
105
+ end
106
+ rescue StandardError => error
107
+ @capistrano.error error.message
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Changelog
4
+ module Notifier
5
+ # The CHANGELOG doesn't include the given version
6
+ class ReleaseNoteNotFound < StandardError; end
7
+ # A git command failed with an exit code higher than 0
8
+ class GitCommandFailed < StandardError; end
9
+
10
+ # A class is not implementing an expected method
11
+ class NotImplementedError < StandardError; end
12
+ end
13
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'titleize'
5
+
6
+ module Changelog
7
+ module Notifier
8
+ module Formatters
9
+ #
10
+ # Format the given release note hash for ActiveRecord as a multiline text
11
+ #
12
+ class ActiveRecord
13
+ def initialize(release_note_hash)
14
+ @release_note_hash = release_note_hash
15
+ end
16
+
17
+ def format
18
+ release_note = description_text + "\n\n"
19
+
20
+ Array(@release_note_hash[:changes]).each do |change, logs|
21
+ release_note << "#{change.capitalize}\n#{logs.join("\n")}\n\n"
22
+ end
23
+
24
+ # TODO : Avoids adding newlines for the last change ...
25
+ release_note.gsub(/\n\z/, '')
26
+ end
27
+
28
+ private
29
+
30
+ def description_text
31
+ "*#{formatted_application_name}* version " \
32
+ "*#{@release_note_hash[:version]}* has just been released by " \
33
+ "*#{@release_note_hash[:author]}* on the " \
34
+ "*#{formatted_release_date}*.\nPlease find bellow the release note:"
35
+ end
36
+
37
+ def formatted_application_name
38
+ @release_note_hash[:application].gsub(/_/, ' ').titleize
39
+ end
40
+
41
+ def formatted_release_date
42
+ Date.parse(@release_note_hash[:date]).strftime('%b %d')
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'titleize'
5
+
6
+ module Changelog
7
+ module Notifier
8
+ module Formatters
9
+ #
10
+ # Format the given release note hash for Slack
11
+ #
12
+ class Slack
13
+ def initialize(release_note_hash)
14
+ @release_note_hash = release_note_hash
15
+ end
16
+
17
+ def format
18
+ {
19
+ attachments: build_attachments,
20
+ text: description_text
21
+ }
22
+ end
23
+
24
+ private
25
+
26
+ def build_attachments
27
+ Array(@release_note_hash[:changes]).map do |change, logs|
28
+ {
29
+ mrkdwn_in: ['text'],
30
+ color: color_for(change),
31
+ title: change.to_s.capitalize,
32
+ fields: logs.map { |log| { title: log } }
33
+ }
34
+ end
35
+ end
36
+
37
+ def color_for(change)
38
+ case change
39
+ when :added then green
40
+ when :changed then yellow
41
+ when :deprecated then yellow
42
+ when :fixed then green
43
+ when :removed then red
44
+ when :security then red
45
+ end
46
+ end
47
+
48
+ def description_text
49
+ "*#{formatted_application_name}* version " \
50
+ "*#{@release_note_hash[:version]}* has just been released by " \
51
+ "*#{@release_note_hash[:author]}* on the " \
52
+ "*#{formatted_release_date}*.\n\nPlease find bellow the release note:"
53
+ end
54
+
55
+ def formatted_application_name
56
+ @release_note_hash[:application].gsub(/_/, ' ').titleize
57
+ end
58
+
59
+ def formatted_release_date
60
+ Date.parse(@release_note_hash[:date]).strftime('%b %d')
61
+ end
62
+
63
+ def green
64
+ '#36a64f'
65
+ end
66
+
67
+ def red
68
+ '#fc464a'
69
+ end
70
+
71
+ def yellow
72
+ '#fd9134'
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open3'
4
+
5
+ module Changelog
6
+ module Notifier
7
+ #
8
+ # Fetches a Git commit author from the current commit.
9
+ #
10
+ class GitCommitAuthorFetcher
11
+ def self.fetch(options = {})
12
+ output = options[:capistrano].capture(
13
+ "git --git-dir=#{options[:path] || '.'} log -1 --pretty=format:'%an'"
14
+ )
15
+
16
+ return nil if output.empty?
17
+
18
+ output.gsub(/(^\'|'$)/, '')
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Changelog
4
+ module Notifier
5
+ #
6
+ # Fetches a Git tag from the current commit being a version.
7
+ #
8
+ class GitTagFetcher
9
+ def self.fetch(options = {})
10
+ output = options[:capistrano].capture(
11
+ "git --git-dir #{options[:path] || '.'} tag --contains HEAD"
12
+ )
13
+
14
+ return nil if output.empty?
15
+
16
+ output.split("\n").detect { |tag| tag =~ /[\d\.]+/ }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Changelog
4
+ module Notifier
5
+ module Parsers
6
+ #
7
+ # Parses the given `content` as Markdown following the specifications from
8
+ # https://keepachangelog.com.
9
+ #
10
+ class Markdown
11
+ def initialize(content)
12
+ @content = content
13
+ end
14
+
15
+ def extract(version)
16
+ version_is_valid?(version) || raise(ArgumentError, 'Invalid version')
17
+
18
+ parse!
19
+
20
+ version_release_note = detect_version_release_note_for(version)
21
+ version_release_note || raise(Changelog::Notifier::ReleaseNoteNotFound)
22
+
23
+ version_release_note_hash = create_hash_from(version_release_note)
24
+
25
+ url = fetch_version_url_for(version)
26
+ version_release_note_hash[:url] = url
27
+
28
+ version_release_note_hash
29
+ end
30
+
31
+ private
32
+
33
+ # See https://keepachangelog.com/en/1.0.0/#how
34
+ def change_types
35
+ %w[
36
+ added
37
+ changed
38
+ deprecated
39
+ removed
40
+ fixed
41
+ security
42
+ ]
43
+ end
44
+
45
+ def create_hash_from(version_release_note)
46
+ sections = version_release_note.split('### ')
47
+
48
+ # Removes trailing whitespaces
49
+ sections = sections.map { |section| section.gsub(/\s+$/, '') }
50
+
51
+ version, date = extract_version_and_date_from(sections.shift)
52
+
53
+ changes = extract_actions_from(sections)
54
+
55
+ { version: version, date: date, changes: changes }
56
+ end
57
+
58
+ def detect_version_release_note_for(version)
59
+ version_number = fetch_version_number_from(version)
60
+
61
+ @versions.detect do |changelog_version|
62
+ changelog_version.start_with?("#{version_number}]") ||
63
+ changelog_version.start_with?("#{version}]")
64
+ end
65
+ end
66
+
67
+ def extract_actions_from(sections)
68
+ sections.each_with_object({}) do |section, accumulator|
69
+ type, logs = section.match(/^(#{change_types.join('|')})\n(.*)/mi)
70
+ &.captures
71
+
72
+ next unless type
73
+
74
+ type = type.downcase.to_sym
75
+
76
+ logs = logs.split(/\s+-/)
77
+ .reject(&:empty?)
78
+ .map { |log| log.gsub(/^\s+/, '') }
79
+ .map { |log| log.gsub("\n", ' ') }
80
+
81
+ accumulator[type] = logs
82
+ end
83
+ end
84
+
85
+ def extract_version_and_date_from(section)
86
+ section.match(/([\d\.]+)\]\s+-\s+([\d+-]+)/)&.captures
87
+ end
88
+
89
+ def fetch_version_number_from(version)
90
+ version.match(/([\d\.]+)/)&.captures&.first
91
+ end
92
+
93
+ def fetch_version_url_for(version)
94
+ version_number = fetch_version_number_from(version)
95
+ @content.match(%r{\[#{version_number}\]: (https://[\S]+)})
96
+ &.captures
97
+ &.first
98
+ end
99
+
100
+ #
101
+ # Split the file on the double dashes, representing version sections.
102
+ #
103
+ def parse!
104
+ @versions = @content.split(/##\s\[/)
105
+ end
106
+
107
+ def version_is_valid?(version)
108
+ version =~ /[\d\.]+/
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Changelog
4
+ module Notifier
5
+ VERSION = '1.5.0'
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: changelog-notifier
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Guillaume Hain
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-12-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: titleize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.4.1
27
+ description: This gem sends the release note, extracted from your CHANGELOG.md file,
28
+ to Slack when deployin with Capistrano
29
+ email:
30
+ - zedtux@zedroot.org
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".gitlab-ci.yml"
37
+ - ".rspec"
38
+ - ".rubocop.yml"
39
+ - CHANGELOG.md
40
+ - CODE_OF_CONDUCT.md
41
+ - Dockerfile
42
+ - Gemfile
43
+ - Gemfile.lock
44
+ - LICENSE.txt
45
+ - README.md
46
+ - Rakefile
47
+ - bin/console
48
+ - bin/setup
49
+ - changelog-notifier.gemspec
50
+ - lib/changelog/notifier.rb
51
+ - lib/changelog/notifier/adapters/active_record.rb
52
+ - lib/changelog/notifier/adapters/base.rb
53
+ - lib/changelog/notifier/adapters/slack.rb
54
+ - lib/changelog/notifier/capistrano.rb
55
+ - lib/changelog/notifier/capistrano/tasks/changelog_notifier.rake
56
+ - lib/changelog/notifier/entrypoints/capistrano.rb
57
+ - lib/changelog/notifier/errors.rb
58
+ - lib/changelog/notifier/formatters/active_record.rb
59
+ - lib/changelog/notifier/formatters/slack.rb
60
+ - lib/changelog/notifier/git_commit_author_fetcher.rb
61
+ - lib/changelog/notifier/git_tag_fetcher.rb
62
+ - lib/changelog/notifier/parsers/markdown.rb
63
+ - lib/changelog/notifier/version.rb
64
+ - screenshots/changelog-notifiier_slack.png
65
+ homepage: https://gitlab.com/zedtux/changelog-notifier
66
+ licenses:
67
+ - MIT
68
+ metadata:
69
+ allowed_push_host: https://rubygems.org
70
+ homepage_uri: https://gitlab.com/zedtux/changelog-notifier
71
+ source_code_uri: https://gitlab.com/zedtux/changelog-notifier
72
+ changelog_uri: https://gitlab.com/zedtux/changelog-notifier/-/blob/master/CHANGELOG.md
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 2.3.0
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubygems_version: 3.0.4
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Sends current version's release note from your CHANGELOG.md file
92
+ test_files: []