jenkins_pivotal 0.1.1

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: f4aa6f4a314a7e6d757565852e0d2dfc4bbf4756
4
+ data.tar.gz: 53d0944baf85bb5664371dc986af6e9b0e4ace48
5
+ SHA512:
6
+ metadata.gz: f64f207f3d23aa36786248f524fcd90978352ecd38eb9586dd764ee7205ad4585656e337d1b9db52abadb2a5772ec58e874d9c5da19a3b92e11e6fbcf4a09f82
7
+ data.tar.gz: cbe639782a2b45a070362af2af3a5177d1a4c220f8a7bd8eb09cc4b6d29e5e60359daabe5ebc271bc9ea984cd43f42a4f05e6cbb5cc61348171a22a2bad526dd
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jenkins_pivotal.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'guard'
8
+ gem 'guard-rspec'
9
+ end
data/Guardfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Mike Wyatt
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Jenkins + PivotalTracker
2
+
3
+ Much like jenkins_tracker, only uses the proper /source_commits endpoint
4
+ and will set the owner to an "acceptor" when an issues is marked as
5
+ delivered.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ gem install jenkins_pivotal
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ In Jenkins, add a post build step like:
16
+
17
+ ```
18
+ jenkins_pivotal --token a1b2c3 --project 1234 --acceptor-token d4e5f6
19
+ ```
20
+
21
+ Help output is as follows.
22
+
23
+ ```
24
+ Usage: bin/jenkins_pivotal [options...]
25
+ -t, --token Tracker API token.
26
+ -p, --project Tracker Project ID.
27
+ -m, --message Message to add.
28
+ -f, --file Read message from file.
29
+ -u, --url URL to browse commit.
30
+ -a, --acceptor-token Tracker token of acceptor.
31
+ -v, --version Display version information.
32
+ -h, --help Display this help message.
33
+ ```
34
+
35
+ - `--url`: Something like `http://example.com/%s`. It will be formatted
36
+ with the SHA1 of the accompanying commit.
37
+ - `--mesage`/`--file`: Add this message above the commit message in
38
+ Pivotal. This is formatted with the ENV, so with EnvInject you can do
39
+ something akin to `--message "This issue has been deployed to
40
+ %{STAGING_URL}"`
41
+
42
+ Only `--token` and `--project` are required. Use `--acceptor-token` if
43
+ you would like to change the owner of issues as they are marked for
44
+ delivery.
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it ( http://github.com/mikew/jenkins_pivotal/fork )
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ root = File.expand_path '../../lib', __FILE__
4
+ $:.unshift(root) unless $:.include?(root)
5
+
6
+ require 'jenkins_pivotal'
7
+
8
+ JenkinsPivotal::Cli.new(ARGV).run!
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jenkins_pivotal/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "jenkins_pivotal"
8
+ spec.version = JenkinsPivotal::VERSION
9
+ spec.authors = ["Mike Wyatt"]
10
+ spec.email = ["wyatt.mike@gmail.com"]
11
+ spec.summary = %q{Jenkins git changelog -> Pivotal Tracker}
12
+ spec.description = %q{Jenkins git changelog -> Pivotal Tracker}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "rest-client", "~> 1.6"
22
+ spec.add_dependency "slop", "~> 3.4"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.5"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "rspec"
27
+ end
@@ -0,0 +1,156 @@
1
+ module JenkinsPivotal
2
+ class Agent
3
+ attr_reader :token, :project, :message, :url, :current_entry, :file,
4
+ :acceptor_token
5
+
6
+ def initialize(options)
7
+ @token = options[:token]
8
+ @project = options[:project].to_i
9
+ @message = options[:message]
10
+ @file = options[:file]
11
+ @url = options[:url]
12
+ @acceptor_token = options[:acceptor_token]
13
+ @current_entry = nil
14
+ end
15
+
16
+ def file_contents
17
+ if file
18
+ File.read(file).strip
19
+ end
20
+ end
21
+
22
+ def browser_url
23
+ if url
24
+ url % current_entry.sha1
25
+ end
26
+ end
27
+
28
+ def client
29
+ @_client ||= Client.new token: token, acceptor_token: acceptor_token
30
+ end
31
+
32
+ def message_to_post
33
+ given_message = nil
34
+
35
+ if message
36
+ given_message = message
37
+ end
38
+
39
+ if file
40
+ given_message = file_contents
41
+ end
42
+
43
+ if given_message
44
+ formatted = given_message % message_variables
45
+ return "#{formatted}\n\n#{current_entry.message}"
46
+ end
47
+
48
+ current_entry.message
49
+ end
50
+
51
+ def message_variables
52
+ env_variables.inject({}) do |memo, (k, v)|
53
+ memo[k.to_sym] = v
54
+ memo
55
+ end
56
+ end
57
+
58
+ def should_deliver(msg)
59
+ ret = []
60
+ refs = msg.scan /\[((?:.*?)([#0-9 ]+)(?:.*?))\]/
61
+
62
+ refs.each do |group|
63
+ if group[0].downcase.include? 'deliver'
64
+ ids = group[1].strip.split
65
+ ret.concat ids.map { |i| i.gsub('#', '').to_i }
66
+ end
67
+ end
68
+
69
+ return ret
70
+ end
71
+
72
+ def run!
73
+ all_entries = []
74
+ changelog_paths.each do |path|
75
+ parser = ChangelogParser.new changelog_path
76
+ all_entries.concat parser.entries
77
+ end
78
+
79
+ all_entries.each do |entry|
80
+ @current_entry = entry
81
+
82
+ payload = {
83
+ source_commit: {
84
+ url: browser_url,
85
+ message: message_to_post,
86
+ author: current_entry.author_name,
87
+ commit_id: current_entry.sha1
88
+ }
89
+ }
90
+
91
+ client.post_source_commits payload
92
+
93
+ should_deliver(current_entry.message).each do |story_id|
94
+ client.deliver_to_acceptor project, story_id
95
+ end
96
+ end
97
+ end
98
+
99
+ def changelog_paths
100
+ # TODO this should be extracted into ChangelogGatherer or something
101
+ if ENV['CHANGELOG_PATH']
102
+ return [ ENV['CHANGELOG_PATH'] ]
103
+ end
104
+
105
+ start_from = 1
106
+ default_changelog = File.join env_variables['JENKINS_HOME'],
107
+ 'jobs', env_variables['JOB_NAME'],
108
+ 'builds', env_variables['BUILD_NUMBER'],
109
+ 'changelog.xml'
110
+
111
+ # If it's the first build, there's nothing to gather.
112
+ if env_variables['BUILD_NUMBER'] == '1'
113
+ return [ default_changelog ]
114
+ end
115
+
116
+ last_success = File.join env_variables['JENKINS_HOME'],
117
+ 'jobs', env_variables['JOB_NAME'],
118
+ 'builds', 'lastSuccessfulBuild'
119
+
120
+ last_success_num = File.readlink last_success
121
+ if last_success_num != '-1'
122
+ # If the lastSuccessfulBuild was the previous build then the
123
+ # changelog will already be adequate.
124
+ if last_success_num.to_i == env_variables['BUILD_NUMBER'].to_i - 1
125
+ return [ default_changelog ]
126
+ else
127
+ start_from = last_success_num.to_i + 1
128
+ end
129
+ end
130
+
131
+ start_from.upto(env_variables['BUILD_NUMBER'].to_i).map do |i|
132
+ File.join env_variables['JENKINS_HOME'],
133
+ 'jobs', env_variables['JOB_NAME'],
134
+ 'builds', i.to_s,
135
+ 'changelog.xml'
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def env_variables
142
+ ENV
143
+ end
144
+
145
+ def changelog_path
146
+ if ENV['CHANGELOG_PATH']
147
+ return ENV['CHANGELOG_PATH']
148
+ end
149
+
150
+ File.join ENV['JENKINS_HOME'],
151
+ 'jobs', ENV['JOB_NAME'],
152
+ 'builds', ENV['BUILD_NUMBER'],
153
+ 'changelog.xml'
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,64 @@
1
+ module JenkinsPivotal
2
+ class ChangelogParser
3
+ attr_reader :entries, :data
4
+
5
+ def initialize(path)
6
+ @data = ''
7
+ @entries = []
8
+
9
+ if File.exists? path
10
+ @data = File.read path
11
+ load_entries
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def load_entries
18
+ blocks = @data.split /^(commit [a-f0-9]{40})/
19
+
20
+ # If the first line is `commit ...`, the first item of the array will
21
+ # be an empty string.
22
+ if '' == blocks[0]
23
+ blocks.shift
24
+ end
25
+
26
+ # The first line may be something else entirely, like:
27
+ # Changes in branch origin/master, between ...
28
+ if !blocks[0].start_with? 'commit'
29
+ blocks.shift
30
+ end
31
+
32
+ blocks.each_slice(2) do |block|
33
+ @entries.push ChangelogEntry.new(block[0] + block[1])
34
+ end
35
+ end
36
+ end
37
+
38
+ class ChangelogEntry
39
+ attr_reader :author, :author_name, :author_email,
40
+ :committer, :committer_name, :committer_email,
41
+ :sha1, :tree, :parent,
42
+ :message
43
+
44
+ def initialize(data)
45
+ @data = data
46
+ @message = data.split("\n\n")[1].strip
47
+ @sha1 = first_from_scan /^commit ([a-f0-9]{40})/
48
+ @tree = first_from_scan /^tree ([a-f0-9]{40})/
49
+ @parent = first_from_scan /^parent ([a-f0-9]{40})/
50
+ @author = first_from_scan /^author ((.+?) <(.+?)>)/
51
+ @author_name = first_from_scan /^author (.+?) </
52
+ @author_email = first_from_scan /^author .+? <(.+?)>/
53
+ @committer = first_from_scan /^committer ((.+?) <(.+?)>)/
54
+ @committer_name = first_from_scan /^committer (.+?) </
55
+ @committer_email = first_from_scan /^committer .+? <(.+?)>/
56
+ end
57
+
58
+ private
59
+
60
+ def first_from_scan(matcher)
61
+ $1.strip if @data =~ matcher
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,41 @@
1
+ require 'slop'
2
+
3
+ module JenkinsPivotal
4
+ class Cli
5
+ attr_reader :options
6
+
7
+ def initialize(items = ARGV)
8
+ @options = Slop.parse(items, help: true) do
9
+ banner "Usage: #{$0} [options...]"
10
+
11
+ on 't', 'token=', 'Tracker API token.'
12
+ on 'p', 'project=', 'Tracker Project ID.', as: :integer
13
+ on 'm', 'message=', 'Message to add.'
14
+ on 'f', 'file=', 'Read message from file.'
15
+ on 'u', 'url=', 'URL to browse commit.'
16
+ on 'a', 'acceptor-token=', 'Tracker token of acceptor.'
17
+
18
+ on 'v', 'version', 'Display version information.' do
19
+ puts "#{$0} #{JenkinsPivotal::VERSION}"
20
+ exit 0
21
+ end
22
+ end
23
+ end
24
+
25
+ def run!
26
+ unless options.token? && options.project?
27
+ puts @options
28
+ exit 1
29
+ end
30
+
31
+ Agent.new(
32
+ token: options[:token],
33
+ project: options[:project],
34
+ message: options[:message],
35
+ file: options[:file],
36
+ url: options[:url],
37
+ acceptor_token: options[:'acceptor-token']
38
+ ).run!
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ require 'json'
2
+ require 'rest-client'
3
+
4
+ module JenkinsPivotal
5
+ class Client
6
+ attr_reader :connection
7
+
8
+ def initialize(options)
9
+ @options = options
10
+ @connection = build_connection options[:token]
11
+ load_acceptor if options[:acceptor_token]
12
+ end
13
+
14
+ def load_acceptor
15
+ acceptor_conn = build_connection @options[:acceptor_token]
16
+ json = JSON.parse acceptor_conn['/me'].get
17
+ @acceptor_id = json['id']
18
+ @acceptor_name = json['name']
19
+ end
20
+
21
+ def post_source_commits(payload)
22
+ begin
23
+ connection['/source_commits'].post payload.to_json
24
+ rescue => e
25
+ puts e.message
26
+ puts e.http_body
27
+ end
28
+ end
29
+
30
+ def deliver_to_acceptor(project, story_id)
31
+ return unless @acceptor_id
32
+
33
+ begin
34
+ endpoint = "projects/#{project}/stories/#{story_id}"
35
+ payload = { owned_by_id: @acceptor_id }
36
+ connection[endpoint].put payload.to_json
37
+ rescue => e
38
+ puts e.message
39
+ puts e.http_body
40
+ end
41
+ end
42
+
43
+ def api_url
44
+ 'https://www.pivotaltracker.com/services/v5'
45
+ end
46
+
47
+ private
48
+
49
+ def build_connection(token)
50
+ headers = {
51
+ 'X-TrackerToken' => token,
52
+ 'Content-Type' => 'application/json'
53
+ }
54
+
55
+ RestClient::Resource.new api_url, headers: headers
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module JenkinsPivotal
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,8 @@
1
+ require 'jenkins_pivotal/agent'
2
+ require 'jenkins_pivotal/changelog_parser'
3
+ require 'jenkins_pivotal/cli'
4
+ require 'jenkins_pivotal/client'
5
+ require 'jenkins_pivotal/version'
6
+
7
+ module JenkinsPivotal
8
+ end
@@ -0,0 +1,53 @@
1
+ commit 9682485f5b8c5078548a4094ceab789a48150aa8
2
+ tree f338db9d1ad3a1518f864576f04d1247a908a6bb
3
+ parent d9ddf907ed650d0ef30f2d4cf7610024eb3dd0d0
4
+ author Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
5
+ committer Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
6
+
7
+ proper user_is_omniscient logic
8
+
9
+ :100644 100644 b49e6da4bc7a90e0bb6a819f4670f55d9fde6501 e3c4b32bba5c005bd571cfa45cca59fa42f29faa M dashboard/views.py
10
+
11
+ commit f56e6f4bc8abeb01d3ef31eaab0aa137c8aad970
12
+ tree 314c398b0db343ca960ebf3a79d547975cb3fa25
13
+ parent cf747b094e18c2055cff517f8b8cd7f06691293c
14
+ author Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
15
+ committer Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
16
+
17
+ Use global showDialog function on calendar
18
+
19
+ :100644 100644 425cec2a745b01ada9618b6262cea1597c4fc434 d2decc09a6f95283f2565f4ee6b6f3720e2751f5 M tcecalendar/static/js/calendar.js
20
+
21
+ commit d9ddf907ed650d0ef30f2d4cf7610024eb3dd0d0
22
+ tree a632c0cbbefb4250433e9b3fe68ce93f10fa7c71
23
+ parent 4bc2782e5896e37947e3bd2273b6a5cfbbd9169d
24
+ author Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
25
+ committer Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
26
+
27
+ show completed tasks on dashboard, omniscient roles see all tasks
28
+
29
+ :100644 100644 ebb22924da0183007bb73600a262f22381b520df b49e6da4bc7a90e0bb6a819f4670f55d9fde6501 M dashboard/views.py
30
+
31
+ commit 4bc2782e5896e37947e3bd2273b6a5cfbbd9169d
32
+ tree ec13b8b32fe46a54846cbddc928eaa0907a93e6d
33
+ parent 928c51bfb5508846240fdbf2f6df650278d62baa
34
+ author Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
35
+ committer Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
36
+
37
+ rebuild stylesheets
38
+
39
+ :100644 100644 a37962188ac93f1beba47e1e37838741c6460ebf 16e597cae082634d6abd4f0b4e489fe007a29f8d M frontend/static/assets/stylesheets/libs/mobile/learning-centre.css
40
+ :100644 100644 bfc39b983f7401594e1e61e76591fa9558113929 a9f2be7fcff4df92a3cef0b8cf94fb8d3f693ee2 M frontend/static/assets/stylesheets/libs/mobile/teachers-lounge.css
41
+ :100644 100644 00b41fa6758ddeed334fba53779edb0d3331c192 db6f3c0a2ff9bee71cabe7acbddba5ca92e3df94 M frontend/static/assets/stylesheets/mobile_TCE.css
42
+ :100644 100644 3e9d93f235b2be103aa17e8e29f848d2309b7e18 91b1070b37a4eb46e85054177d69adee439b80bf M frontend/static/assets/stylesheets/screen-TCE.css
43
+
44
+ commit e26a7acda7bcdf428ad098e6b4eb9b66b7759bc6
45
+ tree 666900a05c95a597b5eb1504d9c7baea54ec833e
46
+ parent d4a6136ac66041ea5729557a1511df23bf6c89e3
47
+ author Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
48
+ committer Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
49
+
50
+ Fix for `incompatible character encodings: ASCII-8BIT and UTF-8`, maybe
51
+
52
+ :100644 100644 a2c5e5813c7a91177bdb0218af463c5baca4072e 868c0ea25a6ee98d61291e95897a1939f12f14a8 M frontend/sass/config-dev.rb
53
+ :100644 100644 95507498a0cb62ef5f469aaa16bdfc33580b8e47 7f9ab18a069fb37e41a42e86b09610a020f0978f M frontend/sass/config.rb
@@ -0,0 +1 @@
1
+ %{foo}
@@ -0,0 +1,10 @@
1
+ commit 9682485f5b8c5078548a4094ceab789a48150aa8
2
+ tree f338db9d1ad3a1518f864576f04d1247a908a6bb
3
+ parent d9ddf907ed650d0ef30f2d4cf7610024eb3dd0d0
4
+ author Mike Wyatt <wyatt.mike@gmail.com> 1387468320 -0400
5
+ committer Joe Smith <joe.smith@gmail.com> 1387468320 -0400
6
+
7
+ proper user_is_omniscient logic
8
+
9
+ :100644 100644 b49e6da4bc7a90e0bb6a819f4670f55d9fde6501 e3c4b32bba5c005bd571cfa45cca59fa42f29faa M dashboard/views.py
10
+
@@ -0,0 +1,149 @@
1
+ require 'spec_helper'
2
+ require 'fakefs/spec_helpers'
3
+
4
+ describe JenkinsPivotal::Agent do
5
+ subject do
6
+ described_class.new(
7
+ token: 'asdf',
8
+ project: '1234',
9
+ message: '%{foo}',
10
+ url: 'http://example.com/%s',
11
+ file: fixture_path('example-message')
12
+ )
13
+ end
14
+
15
+ let(:entry) { JenkinsPivotal::ChangelogEntry.new read_fixture('single-entry') }
16
+
17
+ before do
18
+ subject.stub(:current_entry).and_return entry
19
+ end
20
+
21
+ its(:token) { should == 'asdf' }
22
+ its(:project) { should == 1234 }
23
+ its(:message) { should == '%{foo}' }
24
+ its(:file_contents) { should == read_fixture('example-message').strip }
25
+ its(:browser_url) { should == subject.url % entry.sha1 }
26
+
27
+ describe 'with messages' do
28
+ before do
29
+ subject.stub(:env_variables).and_return 'foo' => 'bar'
30
+ end
31
+
32
+ it 'converts strings to symbols in message_variables' do
33
+ subject.message_variables[:foo].should == 'bar'
34
+ end
35
+
36
+ it 'formats message with ENV' do
37
+ subject.stub(:file).and_return nil
38
+ subject.message_to_post.should == "bar\n\n#{entry.message}"
39
+ end
40
+
41
+ it 'formats file with ENV' do
42
+ subject.stub(:message).and_return nil
43
+ subject.message_to_post.should == "bar\n\n#{entry.message}"
44
+ end
45
+
46
+ it 'simply posts the commit message when neither exist' do
47
+ subject.stub(:file).and_return nil
48
+ subject.stub(:message).and_return nil
49
+ subject.message_to_post.should == entry.message
50
+ end
51
+ end
52
+
53
+ describe 'with deliveries' do
54
+ it 'can tell when an issue is delivered' do
55
+ single = '[delivers #123]'
56
+ multiple = '[#123 delivered] [deliver #234]'
57
+ complex = '[delivers #123 #234] [#345]'
58
+ none = '[#123] not delivered'
59
+
60
+ subject.should_deliver(single).should == [123]
61
+ subject.should_deliver(multiple).should == [123, 234]
62
+ subject.should_deliver(complex).should == [123, 234]
63
+ subject.should_deliver(none).should == []
64
+ end
65
+ end
66
+
67
+ describe 'gathering changelogs' do
68
+ let(:env) do
69
+ {
70
+ 'JENKINS_HOME' => fixture_path('structures')
71
+ }
72
+ end
73
+
74
+ it 'returns the default changelog on first build' do
75
+ stubbed_env = {
76
+ 'BUILD_NUMBER' => '1',
77
+ 'JOB_NAME' => 'first-run'
78
+ }.merge env
79
+
80
+ expected = [ File.join(stubbed_env['JENKINS_HOME'],
81
+ 'jobs', stubbed_env['JOB_NAME'],
82
+ 'builds', stubbed_env['BUILD_NUMBER'],
83
+ 'changelog.xml'
84
+ ) ]
85
+
86
+ subject.stub(:env_variables).and_return stubbed_env
87
+ subject.changelog_paths.should == expected
88
+ end
89
+
90
+ it 'starts from 1 when the lastSuccessfulBuild is -1' do
91
+ stubbed_env = {
92
+ 'BUILD_NUMBER' => '3',
93
+ 'JOB_NAME' => 'cloudy'
94
+ }.merge env
95
+
96
+ last_success = File.join(stubbed_env['JENKINS_HOME'],
97
+ 'jobs', stubbed_env['JOB_NAME'],
98
+ 'builds', 'lastSuccessfulBuild')
99
+ File.should_receive(:readlink, with: last_success).and_return '-1'
100
+
101
+ expected = [
102
+ File.join(stubbed_env['JENKINS_HOME'],
103
+ 'jobs', stubbed_env['JOB_NAME'],
104
+ 'builds', '1',
105
+ 'changelog.xml'),
106
+
107
+ File.join(stubbed_env['JENKINS_HOME'],
108
+ 'jobs', stubbed_env['JOB_NAME'],
109
+ 'builds', '2',
110
+ 'changelog.xml'),
111
+
112
+ File.join(stubbed_env['JENKINS_HOME'],
113
+ 'jobs', stubbed_env['JOB_NAME'],
114
+ 'builds', stubbed_env['BUILD_NUMBER'],
115
+ 'changelog.xml'),
116
+ ]
117
+
118
+ subject.stub(:env_variables).and_return stubbed_env
119
+ subject.changelog_paths.should == expected
120
+ end
121
+
122
+ it 'starts from lastSuccessfulBuild + 1 otherwise' do
123
+ stubbed_env = {
124
+ 'BUILD_NUMBER' => '5',
125
+ 'JOB_NAME' => 'cloudy'
126
+ }.merge env
127
+
128
+ last_success = File.join(stubbed_env['JENKINS_HOME'],
129
+ 'jobs', stubbed_env['JOB_NAME'],
130
+ 'builds', 'lastSuccessfulBuild')
131
+ File.should_receive(:readlink, with: last_success).and_return '3'
132
+
133
+ expected = [
134
+ File.join(stubbed_env['JENKINS_HOME'],
135
+ 'jobs', stubbed_env['JOB_NAME'],
136
+ 'builds', '4',
137
+ 'changelog.xml'),
138
+
139
+ File.join(stubbed_env['JENKINS_HOME'],
140
+ 'jobs', stubbed_env['JOB_NAME'],
141
+ 'builds', stubbed_env['BUILD_NUMBER'],
142
+ 'changelog.xml'),
143
+ ]
144
+
145
+ subject.stub(:env_variables).and_return stubbed_env
146
+ subject.changelog_paths.should == expected
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe JenkinsPivotal::ChangelogParser do
4
+ subject { described_class.new fixture_path('changelog.xml') }
5
+
6
+ its(:entries) { should be_an Array }
7
+ its(:'entries.length') { should == 5 }
8
+ its(:data) { should == read_fixture('changelog.xml') }
9
+ end
10
+
11
+ describe JenkinsPivotal::ChangelogEntry do
12
+ subject { described_class.new read_fixture('single-entry') }
13
+
14
+ its(:author) { should == 'Mike Wyatt <wyatt.mike@gmail.com>' }
15
+ its(:author_name) { should == 'Mike Wyatt' }
16
+ its(:author_email) { should == 'wyatt.mike@gmail.com' }
17
+ its(:committer) { should == 'Joe Smith <joe.smith@gmail.com>' }
18
+ its(:committer_name) { should == 'Joe Smith' }
19
+ its(:committer_email) { should == 'joe.smith@gmail.com' }
20
+ its(:message) { should == 'proper user_is_omniscient logic' }
21
+ its(:sha1) { should == '9682485f5b8c5078548a4094ceab789a48150aa8' }
22
+ its(:tree) { should == 'f338db9d1ad3a1518f864576f04d1247a908a6bb' }
23
+ its(:parent) { should == 'd9ddf907ed650d0ef30f2d4cf7610024eb3dd0d0' }
24
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe JenkinsPivotal::Cli do
4
+ def try_arg(argument, value)
5
+ args = ["--#{argument}", value.to_s]
6
+ described_class.new(args).options[argument].should == value
7
+ end
8
+
9
+ it 'takes --token' do
10
+ try_arg :token, 'ASDF'
11
+ end
12
+
13
+ it 'takes --project' do
14
+ try_arg :project, 1234
15
+ end
16
+
17
+ it 'takes --message' do
18
+ try_arg :message, 'Message'
19
+ end
20
+
21
+ it 'takes --file' do
22
+ try_arg :file, '/a/b/c'
23
+ end
24
+
25
+ it 'takes --url' do
26
+ try_arg :url, 'http://example.com/%s'
27
+ end
28
+
29
+ end
@@ -0,0 +1,12 @@
1
+ require 'jenkins_pivotal'
2
+
3
+ spec_root = File.expand_path '../', __FILE__
4
+ Dir[File.join(spec_root, 'support/**/*.rb')].each { |f| require f }
5
+
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ config.run_all_when_everything_filtered = true
9
+ config.filter_run :focus
10
+
11
+ config.order = 'random'
12
+ end
@@ -0,0 +1,8 @@
1
+ def read_fixture(filename)
2
+ File.read fixture_path(filename)
3
+ end
4
+
5
+ def fixture_path(filename)
6
+ spec_root = File.expand_path '../../', __FILE__
7
+ File.join spec_root, 'fixtures', filename
8
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jenkins_pivotal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Mike Wyatt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '3.4'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '3.4'
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.5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '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: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Jenkins git changelog -> Pivotal Tracker
84
+ email:
85
+ - wyatt.mike@gmail.com
86
+ executables:
87
+ - jenkins_pivotal
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - Guardfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/jenkins_pivotal
98
+ - jenkins_pivotal.gemspec
99
+ - lib/jenkins_pivotal.rb
100
+ - lib/jenkins_pivotal/agent.rb
101
+ - lib/jenkins_pivotal/changelog_parser.rb
102
+ - lib/jenkins_pivotal/cli.rb
103
+ - lib/jenkins_pivotal/client.rb
104
+ - lib/jenkins_pivotal/version.rb
105
+ - spec/fixtures/changelog.xml
106
+ - spec/fixtures/example-message
107
+ - spec/fixtures/single-entry
108
+ - spec/lib/jenkins_pivotal/agent_spec.rb
109
+ - spec/lib/jenkins_pivotal/changelog_parser_spec.rb
110
+ - spec/lib/jenkins_pivotal/cli_spec.rb
111
+ - spec/spec_helper.rb
112
+ - spec/support/fixtures.rb
113
+ homepage: ''
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.0.14
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Jenkins git changelog -> Pivotal Tracker
137
+ test_files:
138
+ - spec/fixtures/changelog.xml
139
+ - spec/fixtures/example-message
140
+ - spec/fixtures/single-entry
141
+ - spec/lib/jenkins_pivotal/agent_spec.rb
142
+ - spec/lib/jenkins_pivotal/changelog_parser_spec.rb
143
+ - spec/lib/jenkins_pivotal/cli_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/support/fixtures.rb