capistrano-committed 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: