capistrano-committed 0.0.3

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: 826501e1548e90eaebadb55f869d10323df83b2f
4
+ data.tar.gz: c3cae25051c1dfbb5ac0c5bea849d6824cd8a48a
5
+ SHA512:
6
+ metadata.gz: 924a0422e7448dea1d2a970a116a81d7daba68994c2000c3655af2f3ff471d1e7575d838c921d3237012ae67199fb0080d5410d3ac00dd4b77d98843c4701a8a
7
+ data.tar.gz: 354ece8591d11a4e8238c0d71dafbca8ef5f76d1c16d56db3e8dd2acf7e378c5c2543c6a00160bb1ed5f9fc950b5706d19265e8e7bcc2eeed0c5df97e7a61faf
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in capistrano-release-log.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Sam Bauers
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 all
13
+ 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 THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Capistrano Committed
2
+
3
+ Capistrano Committed is an extension to Capistrano 3 which helps to determine what you are about to deploy.
4
+
5
+ It creates a report, which lets you know which GitHub commits and pull requests are not yet deployed to the target stage (server).
6
+
7
+ It does this by:
8
+
9
+ 1. reading the revision log on the server;
10
+ 2. getting all the commits on the specified branch from GitHub (via API);
11
+ 3. looking through those commits and finding all the pull requests;
12
+ 4. getting the info and commits in each pull request;
13
+ 5. pumping all that data into a report;
14
+ 6. uploading that report to the server.
15
+
16
+ At the moment this only works with GitHub repositories, if you have another Git service you would like it to support then please submit a pull request.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile (usually in the `:development` group):
21
+
22
+ ```ruby
23
+ gem 'capistrano-committed'
24
+ ```
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install capistrano-committed
33
+
34
+ In your projects Capfile add this line:
35
+
36
+ ```ruby
37
+ require 'capistrano/committed'
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ In `config/deploy.rb` (in Rails) you need to set at least these options:
43
+
44
+ ```ruby
45
+ # This is the GitHub user or organisation for the repository
46
+ set :committed_user, nil
47
+ set :committed_repo, nil
48
+ ```
49
+
50
+ You will usually need to set the `:committed_github_config` option in order to authenticate, this setting is a hash of options which are passed directly to the [GitHub API gem](https://github.com/peter-murach/github). The full list of GitHub API configuration option are in the [GitHub API gem read me file](https://github.com/peter-murach/github#2-configuration).
51
+
52
+ Example of personal access token usage:
53
+
54
+ ```ruby
55
+ set :committed_github_config, {
56
+ :oauth_token => '65741acbd6473216583421cdef'
57
+ }
58
+ ```
59
+
60
+ Example of basic auth usage:
61
+
62
+ ```ruby
63
+ set :committed_github_config, {
64
+ :basic_auth => 'my-username:my-p455w0rd'
65
+ }
66
+ ```
67
+
68
+ The following settings are optional, the default values are shown here:
69
+
70
+ ```ruby
71
+ # This describes the line that we are looking for and matching against to get
72
+ # revision details from the revision log. Grabbing this from Capistrano locales
73
+ # by default.
74
+ set :committed_revision_line, I18n.t('capistrano.revision_log_message')
75
+
76
+ # The config passed to the GitHub API gem - will usually contain auth details.
77
+ set :committed_github_config, {}
78
+
79
+ # How far back in the revision log we should look
80
+ set :committed_revision_limit, 10
81
+
82
+ # How many days beyond the last revision we fetch should we look for commits
83
+ set :committed_commit_buffer, 1
84
+
85
+ # Where to upload the report - '%s' is replaced with `current_path` if present.
86
+ # `nil` will stop the report from uploading at all, and print to STDOUT instead.
87
+ set :committed_output_path, '%s/public/committed.txt'
88
+
89
+ # This is a regexp pattern that describes issue numbers in commit titles and
90
+ # descriptions. This example matches JIRA numbers enclosed in square braces -
91
+ # e.g. "[ABC-12345]" with the part inside the braces being captured "ABC-12345".
92
+ # Setting this to `nil` will disable issue matching altogether. Note that this
93
+ # setting should specify a string, not a Ruby Regexp object. Specifying a Regexp
94
+ # object might work, but it is not tested.
95
+ set :committed_issue_match, '\[\s?([A-Z0-9]+\-[0-9]+)\s?\]'
96
+
97
+ # This is the URL structure for issues which are found. The default is for a
98
+ # JIRA on Demand instance - e.g. https://example.jira.com/browse/ABC-12345
99
+ # "%s" will be replaced with the issue number. Setting this to `nil` will also
100
+ # disable issue matching altogether.
101
+ set :committed_issue_url, 'https://example.jira.com/browse/%s'
102
+ ```
103
+
104
+ Once your required settings are all in place, you can generate a report by running:
105
+
106
+ ```shell
107
+ $ cap <stage> committed:generate
108
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "capistrano/committed"
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
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'capistrano/committed/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "capistrano-committed"
8
+ spec.version = Capistrano::Committed::VERSION
9
+ spec.authors = ["Sam Bauers"]
10
+ spec.email = ["sam@redant.com.au"]
11
+ spec.license = 'MIT'
12
+
13
+ spec.summary = %q{Tells you what Capistrano 3 is going to deploy based on GitHub commits since the last release.}
14
+ spec.description = %q{Tells you what Capistrano 3 is going to deploy based on GitHub commits since the last release.}
15
+ spec.homepage = "https://github.com/sambauers/capistrano-committed"
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 "capistrano", '~> 3.4'
23
+ spec.add_dependency "github_api", "~> 0.12"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.10"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.4"
28
+ end
@@ -0,0 +1,70 @@
1
+ require 'github_api'
2
+
3
+ module Capistrano
4
+ module Committed
5
+ class GithubApi
6
+ def initialize(config_options = {})
7
+ raise TypeError, '`initialize` requires a hash to be passed as the first and only argument' unless config_options.is_a?(Hash)
8
+
9
+ config_options.merge!({
10
+ :adapter => :net_http,
11
+ :ssl => {:verify => false},
12
+ :per_page => 100,
13
+ :user_agent => 'Committed Ruby Gem (via Github API Ruby Gem)'
14
+ })
15
+
16
+ @client = ::Github.new config_options
17
+ end
18
+
19
+ def get_commit(user, repo, sha)
20
+ validate_user_and_repo(user, repo)
21
+ raise TypeError, sprintf('`%s` requires a valid commit SHA.', __callee__) unless sha.is_a?(String)
22
+
23
+ begin
24
+ @client.repos.commits.get(:user => user, :repo => repo, :sha => sha)
25
+ rescue ::Github::Error::GithubError => e
26
+ rescue_github_errors(e)
27
+ end
28
+ end
29
+
30
+ def get_commits_since(user, repo, date, branch = 'master')
31
+ validate_user_and_repo(user, repo)
32
+ date = Time.parse(date) if date.is_a?(String)
33
+ raise TypeError, sprintf('`%s` requires a valid date.', __callee__) unless date.is_a?(Time)
34
+ raise TypeError, sprintf('`%s` requires a valid branch.', __callee__) unless branch.is_a?(String)
35
+
36
+ begin
37
+ @client.repos.commits.list(:user => user, :repo => repo, :sha => branch, :since => date.iso8601)
38
+ rescue ::Github::Error::GithubError => e
39
+ rescue_github_errors(e)
40
+ end
41
+ end
42
+
43
+ def get_pull_request(user, repo, number)
44
+ validate_user_and_repo(user, repo)
45
+ raise TypeError, sprintf('`%s` requires a valid pull request number.', __callee__) unless number.is_a?(Integer)
46
+
47
+ begin
48
+ info = @client.pull_requests.get(:user => user, :repo => repo, :number => number)
49
+ commits = @client.pull_requests.commits(:user => user, :repo => repo, :number => number)
50
+ return {:info => info, :commits => commits}
51
+ rescue ::Github::Error::GithubError => e
52
+ rescue_github_errors(e)
53
+ end
54
+ end
55
+
56
+ def validate_user_and_repo(user, repo)
57
+ raise TypeError, sprintf('`%s` requires a valid GitHub user.', __caller__) unless user.is_a?(String)
58
+ raise TypeError, sprintf('`%s` requires a valid GitHub repository.', __caller__) unless repo.is_a?(String)
59
+ end
60
+
61
+ def rescue_github_errors(e)
62
+ if e.is_a? ::Github::Error::ServiceError
63
+ raise e, 'There seems to be a problem with the GitHub service.'
64
+ elsif e.is_a? ::Github::Error::ClientError
65
+ raise e, 'There seems to be a problem with the request that was made to GitHub, check that your settings are correct.'
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,38 @@
1
+ require 'i18n'
2
+
3
+ en = {
4
+ error: {
5
+ prerequisites: {
6
+ nil: '`:%{variable}` variable is `nil`, it needs to contain the %{name} name.',
7
+ empty: '`:%{variable}` variable is empty, it needs to contain the %{name} name.',
8
+ string: '`:%{variable}` variable is not a string.',
9
+ hash: '`:%{variable}` variable is not a hash.',
10
+ integer: '`:%{variable}` variable is not a integer.',
11
+ string_or_nil: '`:%{variable}` variable is not a string or `nil`.',
12
+ string_or_regexp_or_nil: '`:%{variable}` variable is not a string or `Regexp` object or `nil`.'
13
+ },
14
+ runtime: {
15
+ revisions_empty: 'The %{branch} branch has never been deployed to the %{stage} stage. No log has been generated.',
16
+ revision_commit_missing: 'No commit data has been found for the %{branch} branch on the %{stage} stage. No log has been generated.',
17
+ commits_empty: 'No commit data has been found for the %{branch} branch on the %{stage} stage since %{time}. No log has been generated.'
18
+ }
19
+ },
20
+ output: {
21
+ next_release: 'Next release',
22
+ previous_release: 'Commits before %{time} are omitted from the report ¯\_(ツ)_/¯',
23
+ current_release: 'Release on %{release_time} from commit %{sha} at %{commit_time}',
24
+ pull_request_number: 'Pull Request #%{number}',
25
+ issue_links: 'Issue links:',
26
+ merged_on: 'Merged on: %{time}',
27
+ merged_by: 'Merged by: %{login}',
28
+ commit_sha: 'Commit %{sha}',
29
+ committed_on: 'Committed on: %{time}',
30
+ committed_by: 'Committed by: %{login}'
31
+ }
32
+ }
33
+
34
+ I18n.backend.store_translations(:en, { capistrano: { committed: en } })
35
+
36
+ if I18n.respond_to?(:enforce_available_locales=)
37
+ I18n.enforce_available_locales = true
38
+ end
@@ -0,0 +1,5 @@
1
+ module Capistrano
2
+ module Committed
3
+ VERSION = '0.0.3'
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ require "capistrano/committed/version"
2
+ require 'capistrano/committed/i18n'
3
+ require "capistrano/committed/github_api"
4
+
5
+ module Capistrano
6
+ module Committed
7
+ class << self
8
+ def scan_for_issues(pattern, string)
9
+ raise TypeError, sprintf('`%s` requires a valid pattern.', __callee__) unless pattern.is_a?(String) || pattern.is_a?(Regexp)
10
+ raise TypeError, sprintf('`%s` requires a valid string.', __callee__) unless pattern.is_a?(String)
11
+
12
+ matches = Regexp.new(pattern).match(string)
13
+ return unless matches && matches[1]
14
+ matches = matches.to_a
15
+ matches.shift
16
+ matches
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ load File.expand_path("../tasks/committed.rake", __FILE__)
@@ -0,0 +1,390 @@
1
+ namespace :committed do
2
+ task :check_prerequisites do
3
+ # Checks all the settings to make sure they are OK - mostly just checks type
4
+ {:committed_user => 'user', :committed_repo => 'repository'}.each do |variable, name|
5
+ raise TypeError,
6
+ I18n.t('capistrano.committed.error.prerequisites.nil', variable: variable, name: name) if fetch(variable).nil?
7
+ raise ArgumentError,
8
+ I18n.t('capistrano.committed.error.prerequisites.empty', variable: variable, name: name) if fetch(variable).empty?
9
+ end
10
+
11
+ raise TypeError,
12
+ I18n.t('capistrano.committed.error.prerequisites.string', variable: 'committed_revision_line') unless fetch(:committed_revision_line).is_a?(String)
13
+ raise TypeError,
14
+ I18n.t('capistrano.committed.error.prerequisites.hash', variable: 'committed_github_config') unless fetch(:committed_github_config).is_a?(Hash)
15
+ raise TypeError,
16
+ I18n.t('capistrano.committed.error.prerequisites.integer', variable: 'committed_revision_limit') unless fetch(:committed_revision_limit).is_a?(Integer)
17
+ raise TypeError,
18
+ I18n.t('capistrano.committed.error.prerequisites.integer', variable: 'committed_commit_buffer') unless fetch(:committed_commit_buffer).is_a?(Integer)
19
+ raise TypeError,
20
+ I18n.t('capistrano.committed.error.prerequisites.string_or_nil', variable: 'committed_output_path') unless fetch(:committed_output_path).is_a?(String) || fetch(:committed_output_path).nil?
21
+ raise TypeError,
22
+ I18n.t('capistrano.committed.error.prerequisites.string_or_regexp_or_nil', variable: 'committed_issue_match') unless fetch(:committed_issue_match).is_a?(String) || fetch(:committed_issue_match).is_a?(Regexp) || fetch(:committed_issue_match).nil?
23
+ raise TypeError,
24
+ I18n.t('capistrano.committed.error.prerequisites.string_or_nil', variable: 'committed_issue_url') unless fetch(:committed_issue_url).is_a?(String) || fetch(:committed_issue_url).nil?
25
+ end
26
+
27
+ desc 'Generetes a report of commit and pull request status on the current stage'
28
+ task :generate do
29
+ invoke 'committed:check_prerequisites'
30
+
31
+ # Only do this on the primary web server
32
+ on primary :web do
33
+ # Get the Capistrano revision log
34
+ lines = capture(:cat, revision_log).split("\n").reverse
35
+
36
+ # Build the regex to search for revision data in the log, by default this
37
+ # is the localised string from Capistrano
38
+ search = fetch(:committed_revision_line)
39
+ search = Regexp.escape(search)
40
+ search = search.gsub('%\{', '(?<').gsub('\}', '>.+)')
41
+ search = Regexp.new(search)
42
+
43
+ # Build the revisions hash
44
+ revisions = {}
45
+ lines.each do |line|
46
+ matches = search.match(line)
47
+ next unless matches[:branch].to_s == fetch(:branch).to_s
48
+ revisions[matches[:sha]] = {
49
+ :branch => matches[:branch],
50
+ :sha => matches[:sha],
51
+ :release => matches[:release],
52
+ :user => matches[:user],
53
+ :entries => {}
54
+ }
55
+ # Only store a certain number of revisions
56
+ break if revisions.count == fetch(:committed_revision_limit)
57
+ end
58
+
59
+ # No revisions, no log
60
+ if revisions.empty?
61
+ info I18n.t('capistrano.committed.error.runtime.revisions_empty',
62
+ branch: fetch(:branch).to_s,
63
+ stage: fetch(:stage).to_s)
64
+ return
65
+ end
66
+
67
+ # Sort revisions by release date
68
+ revisions = revisions.sort_by{|sha, matches| matches[:release]}.to_h
69
+ # Add the "next" revision
70
+ revisions.merge!({
71
+ :next => {
72
+ :entries => {}
73
+ }
74
+ })
75
+ # Reverse the order of revisions in the hash (most recent first)
76
+ revisions = revisions.to_a.reverse.to_h
77
+ revisions.merge!({
78
+ :previous => {
79
+ :entries => {}
80
+ }
81
+ })
82
+
83
+ # Initialize the GitHub API client
84
+ github = ::Capistrano::Committed::GithubApi.new(fetch(:committed_github_config))
85
+
86
+ # Get the actual date of the commit referenced to by the revision
87
+ earliest_date = nil
88
+ revisions.each do |sha, revision|
89
+ next if sha == :next || sha == :previous
90
+ commit = github.get_commit(fetch(:committed_user), fetch(:committed_repo), sha)
91
+ unless commit.nil?
92
+ earliest_date = commit[:commit][:committer][:date]
93
+ revisions[sha][:date] = earliest_date
94
+ end
95
+ end
96
+
97
+ # No commit data on revisions, no log
98
+ if earliest_date.nil?
99
+ info I18n.t('capistrano.committed.error.runtime.revision_commit_missing',
100
+ branch: fetch(:branch).to_s,
101
+ stage: fetch(:stage).to_s)
102
+ return
103
+ end
104
+
105
+ # Go back an extra N day
106
+ earliest_date = (Time.parse(earliest_date) - (fetch(:committed_commit_buffer) * 24 * 60 * 60)).iso8601
107
+ revisions[:previous][:date] = earliest_date
108
+
109
+ # Get all the commits on this branch
110
+ commits = github.get_commits_since(fetch(:committed_user),
111
+ fetch(:committed_repo),
112
+ earliest_date,
113
+ fetch(:branch).to_s)
114
+
115
+ # No commits, no log
116
+ if commits.empty?
117
+ info I18n.t('capistrano.committed.error.runtime.commits_empty',
118
+ branch: fetch(:branch).to_s,
119
+ stage: fetch(:stage).to_s,
120
+ time: earliest_date)
121
+ return
122
+ end
123
+
124
+ # Map commits to a hash keyed by sha
125
+ commits = Hash[commits.map { |commit| [commit[:sha], commit] }]
126
+
127
+ # Get all pull requests listed in the commits
128
+ revision_index = 0
129
+ commits.each do |sha, commit|
130
+ # Match to GitHub generated commit message, or don't
131
+ matches = /^Merge pull request \#([0-9]+)/.match(commit[:commit][:message])
132
+ next unless matches && matches[1]
133
+
134
+ # Get the pull request from GitHub
135
+ pull_request = github.get_pull_request(fetch(:committed_user),
136
+ fetch(:committed_repo),
137
+ matches[1].to_i)
138
+
139
+ # Get the previous revisions commit time and the merge time of the pull request
140
+ previous_revision = revisions[revisions.keys[revision_index + 1]]
141
+ previous_revision_date = Time.parse(previous_revision[:date])
142
+ merged_at = Time.parse(pull_request[:info][:merged_at])
143
+
144
+ # Unless this pull request was merged before the previous release reference was committed
145
+ unless merged_at > previous_revision_date
146
+ # Move to the previous revision
147
+ revision_index += 1
148
+ end
149
+
150
+ # Push pull request data in to the revision entries hash
151
+ revisions[revisions.keys[revision_index]][:entries][pull_request[:info][:merged_at]] = [{
152
+ :type => :pull_request,
153
+ :info => pull_request[:info],
154
+ :commits => pull_request[:commits]
155
+ }]
156
+
157
+ # Delete commits which are in this pull request from the hash of commits
158
+ commits.delete(sha)
159
+ next if pull_request[:commits].empty?
160
+ pull_request[:commits].each do |c|
161
+ commits.delete(c[:sha])
162
+ end
163
+ end
164
+
165
+ # Loop through remaining commits and push them into th revision entries hash
166
+ revision_index = 0
167
+ commits.each do |sha, commit|
168
+ previous_revision = revisions[revisions.keys[revision_index + 1]]
169
+ previous_revision_date = Time.parse(previous_revision[:date])
170
+ committed_at = Time.parse(commit[:commit][:committer][:date])
171
+
172
+ unless committed_at > previous_revision_date
173
+ revision_index += 1
174
+ end
175
+
176
+ if revisions[revisions.keys[revision_index]][:entries][commit[:commit][:committer][:date]].nil?
177
+ revisions[revisions.keys[revision_index]][:entries][commit[:commit][:committer][:date]] = []
178
+ end
179
+ revisions[revisions.keys[revision_index]][:entries][commit[:commit][:committer][:date]] << {
180
+ :type => :commit,
181
+ :info => commit
182
+ }
183
+ end
184
+
185
+ # Loop through the revisions to create the output
186
+ output = []
187
+ revisions.each do |sha, revision|
188
+
189
+ # Build the revision header
190
+ output << ''
191
+ output << '==============================================================================================='
192
+ case sha
193
+ when :next
194
+ output << I18n.t('capistrano.committed.output.next_release')
195
+ when :previous
196
+ output << I18n.t('capistrano.committed.output.previous_release',
197
+ time: revision[:date])
198
+ else
199
+ output << I18n.t('capistrano.committed.output.current_release',
200
+ release_time: Time.parse(revision[:release]).iso8601,
201
+ sha: revision[:sha],
202
+ commit_time: revision[:date])
203
+ end
204
+ output << '==============================================================================================='
205
+ output << ''
206
+
207
+ # Loop through the entries in this revision
208
+ revision[:entries].sort_by{|date, entries| date}.reverse.to_h.each do |date, entries|
209
+ entries.each do |entry|
210
+ case entry[:type]
211
+ when :pull_request
212
+ # These are pull requests that are included in this revision
213
+
214
+ # Print out the pull request number and title
215
+ output << sprintf(' * %s',
216
+ I18n.t('capistrano.committed.output.pull_request_number',
217
+ number: entry[:info][:number]))
218
+ output << sprintf(' %s', entry[:info][:title])
219
+ output << ''
220
+
221
+ # Print out each line of the pull request description
222
+ lines = entry[:info][:body].chomp.split("\n")
223
+ unless lines.empty?
224
+ output << sprintf(' %s', lines.join("\n "))
225
+ output << ''
226
+ end
227
+
228
+ unless fetch(:committed_issue_match).nil? || fetch(:committed_issue_url).nil?
229
+ # Get any issue numbers referred to in the commit info and print links to them
230
+ issues = ::Capistrano::Committed.scan_for_issues(fetch(:committed_issue_match),
231
+ entry[:info][:title] + entry[:info][:body])
232
+ unless issues.nil?
233
+ output << sprintf(' %s',
234
+ I18n.t('capistrano.committed.output.issue_links'))
235
+ issues.each do |issue|
236
+ url = sprintf(fetch(:committed_issue_url), issue)
237
+ output << sprintf(' - %s', url)
238
+ end
239
+ output << ''
240
+ end
241
+ end
242
+
243
+ # Merger details
244
+ output << sprintf(' %s',
245
+ I18n.t('capistrano.committed.output.merged_on',
246
+ time: entry[:info][:merged_at]))
247
+ output << sprintf(' %s',
248
+ I18n.t('capistrano.committed.output.merged_by',
249
+ login: entry[:info][:merged_by][:login]))
250
+ output << ''
251
+
252
+ # Print a link to the pull request on GitHub
253
+ output << sprintf(' %s', entry[:info][:html_url])
254
+ output << ''
255
+
256
+ # Loop through the commits in this pull request
257
+ unless entry[:commits].nil?
258
+ entry[:commits].each do |commit|
259
+ output << ' -------------------------------------------------------------------------------------------'
260
+ output << ' |'
261
+
262
+ # Print the commit ref
263
+ output << sprintf(' | * %s',
264
+ I18n.t('capistrano.committed.output.commit_sha',
265
+ sha: commit[:sha]))
266
+ output << ' |'
267
+
268
+ # Print the commit message
269
+ lines = commit[:commit][:message].chomp.split("\n")
270
+ unless lines.empty?
271
+ output << sprintf(' | > %s', lines.join("\n | > "))
272
+ output << ' |'
273
+
274
+ unless fetch(:committed_issue_match).nil? || fetch(:committed_issue_url).nil?
275
+ # Get any issue numbers referred to in the commit message and print links to them
276
+ issues = ::Capistrano::Committed.scan_for_issues(fetch(:committed_issue_match),
277
+ commit[:commit][:message])
278
+ unless issues.nil?
279
+ output << sprintf(' | %s',
280
+ I18n.t('capistrano.committed.output.issue_links'))
281
+ issues.each do |issue|
282
+ url = sprintf(fetch(:committed_issue_url), issue)
283
+ output << sprintf(' | - %s' , url)
284
+ end
285
+ output << ' |'
286
+ end
287
+ end
288
+ end
289
+
290
+ # Committer details
291
+ output << sprintf(' | %s',
292
+ I18n.t('capistrano.committed.output.committed_on',
293
+ time: commit[:commit][:committer][:date]))
294
+ output << sprintf(' | %s',
295
+ I18n.t('capistrano.committed.output.committed_by',
296
+ login: commit[:committer][:login]))
297
+ output << ' |'
298
+
299
+ # Print a link to the commit in GitHub
300
+ output << sprintf(' | %s', commit[:html_url])
301
+ output << ' |'
302
+ end
303
+ output << ' -------------------------------------------------------------------------------------------'
304
+ output << ''
305
+ end
306
+
307
+ when :commit
308
+ # These are commits that are included in this revision, but are not in any pull requests
309
+
310
+ # Print the commit ref
311
+ output << sprintf(' * %s',
312
+ I18n.t('capistrano.committed.output.commit_sha',
313
+ sha: entry[:info][:sha]))
314
+ output << ''
315
+
316
+ # Print the commit message
317
+ lines = entry[:info][:commit][:message].chomp.split("\n")
318
+ unless lines.empty?
319
+ output << sprintf(' > %s', lines.join("\n > "))
320
+ output << ''
321
+
322
+ unless fetch(:committed_issue_match).nil? || fetch(:committed_issue_url).nil?
323
+ # Get any issue numbers referred to in the commit message and print links to them
324
+ issues = ::Capistrano::Committed.scan_for_issues(fetch(:committed_issue_match),
325
+ entry[:info][:commit][:message])
326
+ unless issues.nil?
327
+ output << sprintf(' %s',
328
+ I18n.t('capistrano.committed.output.issue_links'))
329
+ issues.each do |issue|
330
+ url = sprintf(fetch(:committed_issue_url), issue)
331
+ output << sprintf(' - %s', url)
332
+ end
333
+ output << ''
334
+ end
335
+ end
336
+ end
337
+
338
+ # Committer details
339
+ output << sprintf(' %s',
340
+ I18n.t('capistrano.committed.output.committed_on',
341
+ time: entry[:info][:commit][:committer][:date]))
342
+ output << sprintf(' %s',
343
+ I18n.t('capistrano.committed.output.committed_by',
344
+ login: entry[:info][:committer][:login]))
345
+ output << ''
346
+
347
+ # Print a link to the commit in GitHub
348
+ output << sprintf(' %s', entry[:info][:html_url])
349
+ output << ''
350
+ end
351
+
352
+ output << '-----------------------------------------------------------------------------------------------'
353
+ output << ''
354
+ end
355
+ end
356
+
357
+ output << ''
358
+ end
359
+
360
+ # Send the output to screen, or to a file on the server
361
+ if fetch(:committed_output_path).nil?
362
+ # Just print to STDOUT
363
+ puts output
364
+ else
365
+ # Determine the output path and upload the output there
366
+ output_path = sprintf(fetch(:committed_output_path), current_path)
367
+ upload! StringIO.new(output.join("\n")), output_path
368
+
369
+ # Make sure the report is world readable
370
+ execute(:chmod, 'a+r', output_path)
371
+ end
372
+ end
373
+ end
374
+ end
375
+
376
+ # Load the default settings
377
+ namespace :load do
378
+ task :defaults do
379
+ # See README for descriptions of each setting
380
+ set :committed_user, ->{ nil }
381
+ set :committed_repo, ->{ nil }
382
+ set :committed_revision_line, ->{ I18n.t('capistrano.revision_log_message') }
383
+ set :committed_github_config, ->{ {} }
384
+ set :committed_revision_limit, ->{ 10 }
385
+ set :committed_commit_buffer, ->{ 1 }
386
+ set :committed_output_path, ->{ '%s/public/committed.txt' }
387
+ set :committed_issue_match, ->{ '\[\s?([A-Z0-9]+\-[0-9]+)\s?\]' }
388
+ set :committed_issue_url, ->{ 'https://example.jira.com/browse/%s' }
389
+ end
390
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: capistrano-committed
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Sam Bauers
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: capistrano
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: github_api
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.12'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.12'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.10'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.10'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.4'
83
+ description: Tells you what Capistrano 3 is going to deploy based on GitHub commits
84
+ since the last release.
85
+ email:
86
+ - sam@redant.com.au
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - ".rspec"
93
+ - ".travis.yml"
94
+ - Gemfile
95
+ - LICENSE
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - capistrano-committed.gemspec
101
+ - lib/capistrano/committed.rb
102
+ - lib/capistrano/committed/github_api.rb
103
+ - lib/capistrano/committed/i18n.rb
104
+ - lib/capistrano/committed/version.rb
105
+ - lib/capistrano/tasks/committed.rake
106
+ homepage: https://github.com/sambauers/capistrano-committed
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.4.5.1
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: Tells you what Capistrano 3 is going to deploy based on GitHub commits since
130
+ the last release.
131
+ test_files: []
132
+ has_rdoc: