github-pivotal-flow 0.2.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -0
- data/bin/git-publish +6 -0
- data/lib/github_pivotal_flow.rb +1 -0
- data/lib/github_pivotal_flow/publish.rb +32 -0
- data/lib/github_pivotal_flow/start.rb +0 -6
- data/lib/github_pivotal_flow/story.rb +2 -3
- data/lib/github_pivotal_flow/version.rb +1 -1
- data/spec/github_pivotal_flow/command_spec.rb +1 -6
- data/spec/github_pivotal_flow/configuration_spec.rb +16 -16
- data/spec/github_pivotal_flow/finish_spec.rb +12 -13
- data/spec/github_pivotal_flow/git_spec.rb +24 -25
- data/spec/github_pivotal_flow/publish_spec.rb +66 -0
- data/spec/github_pivotal_flow/shell_spec.rb +7 -7
- data/spec/github_pivotal_flow/start_spec.rb +59 -26
- data/spec/github_pivotal_flow/story_spec.rb +28 -30
- metadata +28 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 899947a9ef6741e50d2fcec85f2fc9bbe0b8c402
|
4
|
+
data.tar.gz: c085c1c245c4b4a5bdcee3ca0eaa191bf06393b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 63f8b8d5e05b61e09f4158224d8981aee3d8ce705ee8adbe2e5ede68c4158fb3aed51fc01d1f9229a481a52ec4a5cbad85de8e464a4683d796197fb18e85fe98
|
7
|
+
data.tar.gz: d6384a6ec33fc24e493d1c3edd5ea71d04d3c3a81300cb3a08bd7e67d82f03c956d569f5397c0f9429f99048bc93df8cba231dc01aec4f65fc2049bf273afbd8
|
data/README.md
CHANGED
@@ -27,6 +27,7 @@ $ git start # Creates branch, opens a pull request on Github and starts st
|
|
27
27
|
$ git commit ...
|
28
28
|
$ git commit ... # Your existing development process
|
29
29
|
$ git commit ...
|
30
|
+
$ git publish ... # Push the branch out to origin and open a pull request on github to gather some feedback
|
30
31
|
$ git finish # Merges back into the main branch. Pushes to origin, destroys branch and finishes story.
|
31
32
|
```
|
32
33
|
|
@@ -119,6 +120,10 @@ If it doesn't exist already, a `prepare-commit-msg` commit hook is added to your
|
|
119
120
|
#
|
120
121
|
```
|
121
122
|
|
123
|
+
### `git publish`
|
124
|
+
This command pushes your changes back to the origin, and opens a pull request on Github for your branch.
|
125
|
+
|
126
|
+
|
122
127
|
### `git finish [--no-complete]`
|
123
128
|
This command finishes a story by merging and cleaning up its branch and then pushing the changes to a remote server.
|
124
129
|
This command can be run in two ways. First it can be run without the `--no-complete` option.
|
data/bin/git-publish
ADDED
data/lib/github_pivotal_flow.rb
CHANGED
@@ -44,4 +44,5 @@ require File.join('github_pivotal_flow', 'github_api')
|
|
44
44
|
require File.join('github_pivotal_flow', 'story')
|
45
45
|
require File.join('github_pivotal_flow', 'command')
|
46
46
|
require File.join('github_pivotal_flow', 'start')
|
47
|
+
require File.join('github_pivotal_flow', 'publish')
|
47
48
|
require File.join('github_pivotal_flow', 'finish')
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# The class that encapsulates finishing a Pivotal Tracker Story
|
2
|
+
module GithubPivotalFlow
|
3
|
+
class Publish < Command
|
4
|
+
|
5
|
+
# Publishes the branch and opens the pull request
|
6
|
+
def run!
|
7
|
+
story = @configuration.story
|
8
|
+
fail("Could not find story associated with branch") unless story
|
9
|
+
Git.clean_working_tree?
|
10
|
+
Git.push(story.branch_name, set_upstream: true)
|
11
|
+
unless story.release?
|
12
|
+
print "Creating pull-request on Github... "
|
13
|
+
pull_request_params = story.params_for_pull_request.merge(project: @configuration.project)
|
14
|
+
@configuration.github_client.create_pullrequest(pull_request_params)
|
15
|
+
puts 'OK'
|
16
|
+
end
|
17
|
+
return 0
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def parse_argv(*args)
|
23
|
+
OptionParser.new do |opts|
|
24
|
+
opts.banner = "Usage: git publish [options]"
|
25
|
+
opts.on("-t", "--api-token=", "Pivotal Tracker API key") { |k| options[:api_token] = k }
|
26
|
+
opts.on("-p", "--project-id=", "Pivotal Tracker project id") { |p| options[:project_id] = p }
|
27
|
+
|
28
|
+
opts.on_tail("-h", "--help", "This usage guide") { put opts.to_s; exit 0 }
|
29
|
+
end.parse!(args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -10,12 +10,6 @@ module GithubPivotalFlow
|
|
10
10
|
story.create_branch!
|
11
11
|
@configuration.story = story # Tag the branch with story attributes
|
12
12
|
Git.add_hook 'prepare-commit-msg', File.join(File.dirname(__FILE__), 'prepare-commit-msg.sh')
|
13
|
-
unless story.release?
|
14
|
-
print "Creating pull-request on Github... "
|
15
|
-
pull_request_params = story.params_for_pull_request.merge(project: @configuration.project)
|
16
|
-
@configuration.github_client.create_pullrequest(pull_request_params)
|
17
|
-
puts 'OK'
|
18
|
-
end
|
19
13
|
story.mark_started!
|
20
14
|
return 0
|
21
15
|
end
|
@@ -91,7 +91,6 @@ module GithubPivotalFlow
|
|
91
91
|
Git.set_config('root-branch', root_branch_name, :branch)
|
92
92
|
Git.set_config('root-remote', root_origin, :branch)
|
93
93
|
Git.commit(commit_message: commit_message, allow_empty: true)
|
94
|
-
Git.push(branch_name, set_upstream: true)
|
95
94
|
end
|
96
95
|
|
97
96
|
def merge_to_root!(commit_message = nil, options = {})
|
@@ -164,8 +163,8 @@ module GithubPivotalFlow
|
|
164
163
|
# For release branches the format is release/5.0
|
165
164
|
"#{Git.get_config(KEY_RELEASE_PREFIX, :inherited)}#{branch_name}"
|
166
165
|
else
|
167
|
-
n = "#{branch_prefix}
|
168
|
-
n << "
|
166
|
+
n = "#{branch_prefix}"
|
167
|
+
n << "#{branch_name}" unless branch_name.blank?
|
169
168
|
n
|
170
169
|
end
|
171
170
|
end
|
@@ -5,15 +5,10 @@ module GithubPivotalFlow
|
|
5
5
|
before do
|
6
6
|
$stdout = StringIO.new
|
7
7
|
$stderr = StringIO.new
|
8
|
-
@configuration = double('configuration')
|
9
8
|
@project = double('project')
|
9
|
+
@configuration = double('configuration', validate: true, project_id: 123456, project: @project)
|
10
10
|
allow(Configuration).to receive(:new).and_return(@configuration)
|
11
11
|
allow(PivotalTracker::Project).to receive(:find).and_return(@project)
|
12
|
-
@configuration.stub(
|
13
|
-
validate: true,
|
14
|
-
project_id: 123456,
|
15
|
-
project: @project,
|
16
|
-
)
|
17
12
|
end
|
18
13
|
|
19
14
|
describe '#initialize' do
|
@@ -16,7 +16,7 @@ module GithubPivotalFlow
|
|
16
16
|
|
17
17
|
describe '#api_token' do
|
18
18
|
it 'does not prompt the user for the API token if it is already configured' do
|
19
|
-
Git.
|
19
|
+
expect(Git).to receive(:get_config).with('pivotal.api-token', :inherited).and_return('test_api_token')
|
20
20
|
|
21
21
|
api_token = @configuration.api_token
|
22
22
|
|
@@ -24,9 +24,9 @@ module GithubPivotalFlow
|
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'prompts the user for the API token if it is not configured and stores it in the local git config' do
|
27
|
-
Git.
|
28
|
-
@configuration.
|
29
|
-
Git.
|
27
|
+
expect(Git).to receive(:get_config).with('pivotal.api-token', :inherited).and_return('')
|
28
|
+
expect(@configuration).to receive(:ask).and_return('test_api_token')
|
29
|
+
expect(Git).to receive(:set_config).with('pivotal.api-token', 'test_api_token', :local)
|
30
30
|
api_token = @configuration.api_token
|
31
31
|
expect(api_token).to eq('test_api_token')
|
32
32
|
end
|
@@ -34,22 +34,22 @@ module GithubPivotalFlow
|
|
34
34
|
|
35
35
|
describe '#project_id' do
|
36
36
|
it 'does not prompt the user for the project id if it is already configured' do
|
37
|
-
Git.
|
37
|
+
expect(Git).to receive(:get_config).with('pivotal.project-id', :inherited).and_return('test_project_id')
|
38
38
|
project_id = @configuration.project_id
|
39
39
|
expect(project_id).to eq('test_project_id')
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'prompts the user for the project id if it is not configured' do
|
43
|
-
Git.
|
43
|
+
expect(Git).to receive(:get_config).with('pivotal.project-id', :inherited).and_return('')
|
44
44
|
menu = double('menu')
|
45
|
-
menu.
|
46
|
-
PivotalTracker::Project.
|
45
|
+
expect(menu).to receive(:prompt=)
|
46
|
+
expect(PivotalTracker::Project).to receive(:all).and_return([
|
47
47
|
PivotalTracker::Project.new(:id => 'id-2', :name => 'name-2'),
|
48
48
|
PivotalTracker::Project.new(:id => 'id-1', :name => 'name-1')])
|
49
|
-
menu.
|
50
|
-
menu.
|
51
|
-
@configuration.
|
52
|
-
Git.
|
49
|
+
expect(menu).to receive(:choice).with('name-1')
|
50
|
+
expect(menu).to receive(:choice).with('name-2')
|
51
|
+
expect(@configuration).to receive(:choose) { |&arg| arg.call menu }.and_return('test_project_id')
|
52
|
+
expect(Git).to receive(:set_config).with('pivotal.project-id', 'test_project_id', :local)
|
53
53
|
|
54
54
|
project_id = @configuration.project_id
|
55
55
|
|
@@ -86,7 +86,7 @@ module GithubPivotalFlow
|
|
86
86
|
end
|
87
87
|
|
88
88
|
it 'uses the branch name to deduce the story id if no git config is found' do
|
89
|
-
Git.
|
89
|
+
allow(Git).to receive(:current_branch).and_return('feature/12345678-sample_feature')
|
90
90
|
expect(Git).to receive(:get_config).with('pivotal-story-id', :branch).and_return(' ')
|
91
91
|
expect(project).to receive(:stories).and_return(stories)
|
92
92
|
expect(stories).to receive(:find).with(12345678).and_return(pivotal_story)
|
@@ -97,7 +97,7 @@ module GithubPivotalFlow
|
|
97
97
|
end
|
98
98
|
|
99
99
|
it 'prompts for the story id if the branch name does not match the known format' do
|
100
|
-
Git.
|
100
|
+
allow(Git).to receive(:current_branch).and_return('unknownformat')
|
101
101
|
expect(Git).to receive(:get_config).with('pivotal-story-id', :branch).and_return(' ')
|
102
102
|
expect(@configuration).to receive(:ask).and_return('12345678')
|
103
103
|
expect(Git).to receive(:set_config).with('pivotal-story-id', '12345678', :branch)
|
@@ -112,8 +112,8 @@ module GithubPivotalFlow
|
|
112
112
|
|
113
113
|
describe '#project' do
|
114
114
|
it 'supports working with git urls from the configuration' do
|
115
|
-
Git.
|
116
|
-
Git.
|
115
|
+
expect(Git).to receive(:get_remote).and_return('origin')
|
116
|
+
expect(Git).to receive(:get_config).with('remote.origin.url').and_return('git@github.com:roomorama/github-pivotal-flow.git')
|
117
117
|
project = @configuration.project
|
118
118
|
expect(project.owner).to eq('roomorama')
|
119
119
|
expect(project.name).to eq('github-pivotal-flow')
|
@@ -9,19 +9,18 @@ module GithubPivotalFlow
|
|
9
9
|
|
10
10
|
@project = double('project')
|
11
11
|
@story = double('story', branch_name: 'feature/1234-sample_story')
|
12
|
-
@configuration = double('configuration'
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
validate: true,
|
12
|
+
@configuration = double('configuration',
|
13
|
+
development_branch: 'development',
|
14
|
+
master_branch: 'master',
|
15
|
+
feature_prefix: 'feature/',
|
16
|
+
hotfix_prefix: 'hotfix/',
|
17
|
+
release_prefix: 'release/',
|
18
|
+
api_token: 'token',
|
19
|
+
project_id: '123',
|
20
|
+
project: @project,
|
21
|
+
github_client: @ghclient,
|
22
|
+
story: @story,
|
23
|
+
validate: true
|
25
24
|
)
|
26
25
|
allow(Configuration).to receive(:new).and_return(@configuration)
|
27
26
|
allow(Project).to receive(:find).and_return(@project)
|
@@ -27,7 +27,7 @@ module GithubPivotalFlow
|
|
27
27
|
git_directory = File.expand_path '.git', root
|
28
28
|
Dir.mkdir git_directory
|
29
29
|
|
30
|
-
Dir.
|
30
|
+
expect(Dir).to receive(:pwd).and_return(child_directory)
|
31
31
|
|
32
32
|
repository_root = Git.repository_root
|
33
33
|
|
@@ -40,7 +40,7 @@ module GithubPivotalFlow
|
|
40
40
|
child_directory = File.expand_path 'child', root
|
41
41
|
Dir.mkdir child_directory
|
42
42
|
|
43
|
-
Dir.
|
43
|
+
expect(Dir).to receive(:pwd).and_return(child_directory)
|
44
44
|
|
45
45
|
expect { Git.repository_root }.to raise_error
|
46
46
|
end
|
@@ -49,8 +49,8 @@ module GithubPivotalFlow
|
|
49
49
|
|
50
50
|
describe '.get_config' do
|
51
51
|
it 'gets configuration scoped by branch when :branch scope is specified' do
|
52
|
-
Git.
|
53
|
-
Shell.
|
52
|
+
expect(Git).to receive(:current_branch).and_return('test_branch_name')
|
53
|
+
expect(Shell).to receive(:exec).with('git config branch.test_branch_name.test_key', false).and_return('test_value')
|
54
54
|
|
55
55
|
value = Git.get_config 'test_key', :branch
|
56
56
|
|
@@ -58,7 +58,7 @@ module GithubPivotalFlow
|
|
58
58
|
end
|
59
59
|
|
60
60
|
it 'gets configuration when :inherited scope is specified' do
|
61
|
-
Shell.
|
61
|
+
expect(Shell).to receive(:exec).with('git config test_key', false).and_return('test_value')
|
62
62
|
|
63
63
|
value = Git.get_config 'test_key', :inherited
|
64
64
|
|
@@ -72,14 +72,14 @@ module GithubPivotalFlow
|
|
72
72
|
|
73
73
|
describe '.set_config' do
|
74
74
|
it 'sets configuration when :branch scope is specified' do
|
75
|
-
Git.
|
76
|
-
Shell.
|
75
|
+
expect(Git).to receive(:current_branch).and_return('test_branch_name')
|
76
|
+
expect(Shell).to receive(:exec).with('git config --local branch.test_branch_name.test_key test_value', true)
|
77
77
|
|
78
78
|
Git.set_config 'test_key', 'test_value', :branch
|
79
79
|
end
|
80
80
|
|
81
81
|
it 'sets configuration when :global scope is specified' do
|
82
|
-
Shell.
|
82
|
+
expect(Shell).to receive(:exec).with('git config --global test_key test_value', true)
|
83
83
|
|
84
84
|
Git.set_config 'test_key', 'test_value', :global
|
85
85
|
end
|
@@ -97,55 +97,54 @@ module GithubPivotalFlow
|
|
97
97
|
|
98
98
|
describe '.create_branch' do
|
99
99
|
it 'creates a branch' do
|
100
|
-
Git.
|
101
|
-
Shell.
|
100
|
+
allow(Git).to receive(:current_branch).and_return('master')
|
101
|
+
expect(Shell).to receive(:exec).with('git branch --quiet dev_branch', true)
|
102
102
|
Git.create_branch 'dev_branch'
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
106
|
-
#FIXME: Can't seem to set an expectation on File for some reason...
|
107
106
|
describe '.add_hook' do
|
108
107
|
it 'does not add a hook if it already exists' do
|
109
108
|
Dir.mktmpdir do |root|
|
110
|
-
Git.
|
109
|
+
expect(Git).to receive(:repository_root).and_return(root)
|
111
110
|
hook = "#{root}/.git/hooks/prepare-commit-msg"
|
112
111
|
expect(File).to receive(:exist?).with(hook).and_return(true)
|
113
112
|
|
114
113
|
Git.add_hook 'prepare-commit-msg', __FILE__
|
115
114
|
allow(File).to receive(:exist?).and_call_original
|
116
|
-
expect(File.exist?(hook)).to
|
115
|
+
expect(File.exist?(hook)).to be_falsy
|
117
116
|
end
|
118
117
|
end
|
119
118
|
|
120
119
|
it 'adds a hook if it does not exist' do
|
121
120
|
Dir.mktmpdir do |root|
|
122
|
-
Git.
|
121
|
+
expect(Git).to receive(:repository_root).and_return(root)
|
123
122
|
hook = "#{root}/.git/hooks/prepare-commit-msg"
|
124
123
|
#File.should_receive(:exist?).with(hook).and_return(false)
|
125
124
|
expect(File).to receive(:exist?).with(hook).and_return(false)
|
126
125
|
|
127
126
|
Git.add_hook 'prepare-commit-msg', __FILE__
|
128
127
|
expect(File).to receive(:exist?).with(hook).and_call_original
|
129
|
-
expect(File.exist?(hook)).to
|
128
|
+
expect(File.exist?(hook)).to be_truthy
|
130
129
|
end
|
131
130
|
end
|
132
131
|
|
133
132
|
it 'adds a hook if it already exists and overwrite is true' do
|
134
133
|
Dir.mktmpdir do |root|
|
135
|
-
Git.
|
134
|
+
expect(Git).to receive(:repository_root).and_return(root)
|
136
135
|
hook = "#{root}/.git/hooks/prepare-commit-msg"
|
137
136
|
|
138
137
|
Git.add_hook 'prepare-commit-msg', __FILE__, true
|
139
138
|
|
140
|
-
File.
|
141
|
-
expect(File.exist?(hook)).to
|
139
|
+
expect(File).to receive(:exist?).and_call_original
|
140
|
+
expect(File.exist?(hook)).to be_truthy
|
142
141
|
end
|
143
142
|
end
|
144
143
|
end
|
145
144
|
|
146
145
|
describe '.merge' do
|
147
146
|
it 'merges branches' do
|
148
|
-
Shell.
|
147
|
+
expect(Shell).to receive(:exec).with("git merge --quiet --no-ff -m \"Merge development_branch to master\" development_branch", true)
|
149
148
|
|
150
149
|
Git.merge 'development_branch', commit_message: 'Merge development_branch to master', no_ff: true
|
151
150
|
end
|
@@ -153,23 +152,23 @@ module GithubPivotalFlow
|
|
153
152
|
|
154
153
|
describe '.push' do
|
155
154
|
before do
|
156
|
-
Git.
|
155
|
+
expect(Git).to receive(:get_config).with('remote', :branch).and_return('origin')
|
157
156
|
end
|
158
157
|
|
159
158
|
it 'pushes changes back to the origin without refs' do
|
160
|
-
Shell.
|
159
|
+
expect(Shell).to receive(:exec).with('git push --quiet origin ', true)
|
161
160
|
|
162
161
|
Git.push
|
163
162
|
end
|
164
163
|
|
165
164
|
it 'pushes changes back to the origin with refs' do
|
166
|
-
Shell.
|
165
|
+
expect(Shell).to receive(:exec).with('git push --quiet origin foo bar', true)
|
167
166
|
|
168
167
|
Git.push 'foo', 'bar'
|
169
168
|
end
|
170
169
|
|
171
170
|
it 'supports passing in the set_upstream option after a ref' do
|
172
|
-
Shell.
|
171
|
+
expect(Shell).to receive(:exec).with('git push --quiet -u origin foo', true)
|
173
172
|
|
174
173
|
Git.push 'foo', set_upstream: true
|
175
174
|
end
|
@@ -177,7 +176,7 @@ module GithubPivotalFlow
|
|
177
176
|
|
178
177
|
describe '.commit' do
|
179
178
|
it 'creates a commit' do
|
180
|
-
Shell.
|
179
|
+
expect(Shell).to receive(:exec).with("git commit --quiet --allow-empty -m \"test_message\"", true)
|
181
180
|
|
182
181
|
Git.commit commit_message: 'test_message', allow_empty: true
|
183
182
|
end
|
@@ -191,7 +190,7 @@ module GithubPivotalFlow
|
|
191
190
|
|
192
191
|
describe '.tag' do
|
193
192
|
it 'creates a tag' do
|
194
|
-
Shell.
|
193
|
+
expect(Shell).to receive(:exec).with('git tag 1.0.0.RELEASE', true)
|
195
194
|
|
196
195
|
Git.tag '1.0.0.RELEASE'
|
197
196
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module GithubPivotalFlow
|
4
|
+
describe Publish do
|
5
|
+
before do
|
6
|
+
$stdout = StringIO.new
|
7
|
+
$stderr = StringIO.new
|
8
|
+
|
9
|
+
@project = double('project')
|
10
|
+
@story = double('story',
|
11
|
+
branch_name: 'feature/1234-sample_story',
|
12
|
+
release?: false,
|
13
|
+
params_for_pull_request: {})
|
14
|
+
@ghclient = double('ghclient')
|
15
|
+
@ghproject = double('ghproject')
|
16
|
+
@configuration = double('configuration',
|
17
|
+
development_branch: 'development',
|
18
|
+
master_branch: 'master',
|
19
|
+
feature_prefix: 'feature/',
|
20
|
+
hotfix_prefix: 'hotfix/',
|
21
|
+
release_prefix: 'release/',
|
22
|
+
api_token: 'token',
|
23
|
+
project_id: '123',
|
24
|
+
project: @project,
|
25
|
+
github_client: @ghclient,
|
26
|
+
story: @story,
|
27
|
+
validate: true
|
28
|
+
)
|
29
|
+
allow(Configuration).to receive(:new).and_return(@configuration)
|
30
|
+
allow(Project).to receive(:find).and_return(@project)
|
31
|
+
allow(@ghclient).to receive(:create_pullrequest).and_return(true)
|
32
|
+
allow(Git).to receive(:push).and_return(true)
|
33
|
+
@publish = Publish.new
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'fails if we cannot find the story this branch relates to' do
|
37
|
+
allow(@configuration).to receive(:story).and_return(nil)
|
38
|
+
expect { @publish.run! }.to raise_error("Could not find story associated with branch")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'fails with a dirty working tree' do
|
42
|
+
allow(Shell).to receive(:exec).with("git diff --no-ext-diff --ignore-submodules --quiet --exit-code", false).and_return(true)
|
43
|
+
allow(Shell).to receive(:exec).with("git diff-index --cached --quiet --ignore-submodules HEAD --", false).and_raise "fatal: Index contains uncommited changes. Aborting."
|
44
|
+
expect { @publish.run! }.to raise_error(RuntimeError)
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'with a clean working tree' do
|
48
|
+
before do
|
49
|
+
allow(Git).to receive(:clean_working_tree?).and_return(true)
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'pushes the branch back to the origin and sets the upstream' do
|
54
|
+
expect(Git).to receive(:push).with(instance_of(String), hash_including(set_upstream: true)).and_return(true)
|
55
|
+
|
56
|
+
@publish.run!
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'opens a pull request' do
|
60
|
+
expect(@ghclient).to receive(:create_pullrequest).and_return(true)
|
61
|
+
|
62
|
+
@publish.run!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -9,8 +9,8 @@ module GithubPivotalFlow
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'should return result when exit code is 0' do
|
12
|
-
Shell.
|
13
|
-
|
12
|
+
expect(Shell).to receive(:`).with('test_command').and_return('test_result')
|
13
|
+
expect($?).to receive(:exitstatus).and_return(0)
|
14
14
|
|
15
15
|
result = Shell.exec 'test_command'
|
16
16
|
|
@@ -18,17 +18,17 @@ module GithubPivotalFlow
|
|
18
18
|
end
|
19
19
|
|
20
20
|
it "should abort with 'FAIL' when the exit code is not 0" do
|
21
|
-
Shell.
|
22
|
-
|
21
|
+
expect(Shell).to receive(:`).with('test_command')
|
22
|
+
expect($?).to receive(:exitstatus).and_return(-1)
|
23
23
|
|
24
|
-
|
24
|
+
expect { Shell.exec 'test_command' }.to raise_error
|
25
25
|
|
26
26
|
expect($stderr.string).to match(/FAIL/)
|
27
27
|
end
|
28
28
|
|
29
29
|
it 'should return result when the exit code is not 0 and told not to abort on failure' do
|
30
|
-
Shell.
|
31
|
-
|
30
|
+
expect(Shell).to receive(:`).with('test_command')
|
31
|
+
expect($?).to receive(:exitstatus).and_return(-1)
|
32
32
|
|
33
33
|
Shell.exec 'test_command', false
|
34
34
|
end
|
@@ -5,46 +5,79 @@ module GithubPivotalFlow
|
|
5
5
|
before do
|
6
6
|
$stdout = StringIO.new
|
7
7
|
$stderr = StringIO.new
|
8
|
-
@story = double('story'
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
@story = double('story',
|
9
|
+
unestimated?: false,
|
10
|
+
release?: false
|
11
|
+
)
|
12
|
+
@project = double('project',
|
13
|
+
stories: [@story]
|
12
14
|
)
|
13
15
|
@ghclient = double('ghclient')
|
14
16
|
@ghproject = double('ghproject')
|
15
|
-
@configuration = double('configuration'
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
validate: true,
|
17
|
+
@configuration = double('configuration',
|
18
|
+
development_branch: 'development',
|
19
|
+
master_branch: 'master',
|
20
|
+
feature_prefix: 'feature/',
|
21
|
+
hotfix_prefix: 'hotfix/',
|
22
|
+
release_prefix: 'release/',
|
23
|
+
api_token: 'token',
|
24
|
+
project_id: '123',
|
25
|
+
project: @project,
|
26
|
+
github_client: @ghclient,
|
27
|
+
story: @story,
|
28
|
+
validate: true
|
28
29
|
)
|
29
30
|
allow(Configuration).to receive(:new).and_return(@configuration)
|
30
31
|
allow(PivotalTracker::Project).to receive(:find).and_return(@project)
|
32
|
+
allow(@story).to receive(:create_branch!).and_return(true)
|
33
|
+
allow(Git).to receive(:add_hook)
|
34
|
+
allow(@configuration).to receive(:story=).with(@story).and_return(true)
|
35
|
+
allow(@story).to receive(:mark_started!)
|
31
36
|
@start = Start.new()
|
32
37
|
end
|
33
38
|
|
34
|
-
it '
|
39
|
+
it 'selects the story and pretty prints out the options' do
|
35
40
|
@start.options[:args] = 'test_filter'
|
36
|
-
@story.stub(:unestimated? => false, :release? => false, params_for_pull_request: {})
|
37
|
-
|
38
41
|
expect(Story).to receive(:select_story).with(@project, 'test_filter').and_return(@story)
|
39
42
|
expect(Story).to receive(:pretty_print)
|
40
|
-
expect(@story).to receive(:create_branch!)
|
41
|
-
expect(Git).to receive(:add_hook)
|
42
|
-
expect(@configuration).to receive(:story=).with(@story).and_return(true)
|
43
|
-
#@story.should_receive(:params_for_pull_request).and_return({})
|
44
|
-
expect(@ghclient).to receive(:create_pullrequest)
|
45
|
-
expect(@story).to receive(:mark_started!)
|
46
43
|
|
47
44
|
@start.run!
|
48
45
|
end
|
46
|
+
|
47
|
+
context 'with a story selected' do
|
48
|
+
before do
|
49
|
+
@start.options[:args] = 'test_filter'
|
50
|
+
allow(Story).to receive(:select_story).with(@project, 'test_filter').and_return(@story)
|
51
|
+
allow(Story).to receive(:pretty_print)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should runs correctly' do
|
55
|
+
@start.run!
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'creates the branch for the story' do
|
59
|
+
expect(@story).to receive(:create_branch!)
|
60
|
+
|
61
|
+
@start.run!
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'adds the Git hook' do
|
65
|
+
expect(Git).to receive(:add_hook)
|
66
|
+
|
67
|
+
@start.run!
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'stores the current story in the configuration' do
|
71
|
+
expect(@configuration).to receive(:story=).with(@story).and_return(true)
|
72
|
+
|
73
|
+
@start.run!
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'marks the story as started in tracker' do
|
77
|
+
expect(@story).to receive(:mark_started!)
|
78
|
+
|
79
|
+
@start.run!
|
80
|
+
end
|
81
|
+
end
|
49
82
|
end
|
50
83
|
end
|
@@ -19,9 +19,9 @@ module GithubPivotalFlow
|
|
19
19
|
describe '.pretty_print' do
|
20
20
|
it 'pretty-prints story information' do
|
21
21
|
story = double('story')
|
22
|
-
story.
|
23
|
-
story.
|
24
|
-
PivotalTracker::Note.
|
22
|
+
expect(story).to receive(:name)
|
23
|
+
expect(story).to receive(:description).and_return("description-1\ndescription-2")
|
24
|
+
expect(PivotalTracker::Note).to receive(:all).and_return([PivotalTracker::Note.new(:noted_at => Date.new, :text => 'note-1')])
|
25
25
|
|
26
26
|
Story.pretty_print story
|
27
27
|
|
@@ -35,9 +35,9 @@ module GithubPivotalFlow
|
|
35
35
|
|
36
36
|
it 'does not pretty print description or notes if there are none (empty)' do
|
37
37
|
story = double('story')
|
38
|
-
story.
|
39
|
-
story.
|
40
|
-
PivotalTracker::Note.
|
38
|
+
expect(story).to receive(:name)
|
39
|
+
expect(story).to receive(:description)
|
40
|
+
expect(PivotalTracker::Note).to receive(:all).and_return([])
|
41
41
|
|
42
42
|
Story.pretty_print story
|
43
43
|
|
@@ -48,9 +48,9 @@ module GithubPivotalFlow
|
|
48
48
|
|
49
49
|
it 'does not pretty print description or notes if there are none (nil)' do
|
50
50
|
story = double('story')
|
51
|
-
story.
|
52
|
-
story.
|
53
|
-
PivotalTracker::Note.
|
51
|
+
expect(story).to receive(:name)
|
52
|
+
expect(story).to receive(:description).and_return('')
|
53
|
+
expect(PivotalTracker::Note).to receive(:all).and_return([])
|
54
54
|
|
55
55
|
Story.pretty_print story
|
56
56
|
|
@@ -125,48 +125,46 @@ module GithubPivotalFlow
|
|
125
125
|
|
126
126
|
describe '#create_branch!' do
|
127
127
|
before do
|
128
|
-
Git.
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
)
|
138
|
-
@pivotal_story.
|
139
|
-
|
140
|
-
id: '123456',
|
141
|
-
name: 'test',
|
142
|
-
description: 'description')
|
128
|
+
allow(Git).to receive(:checkout).and_return(nil)
|
129
|
+
allow(Git).to receive(:pull_remote).and_return(nil)
|
130
|
+
allow(Git).to receive(:create_branch).and_return(nil)
|
131
|
+
allow(Git).to receive(:set_config).and_return(nil)
|
132
|
+
allow(Git).to receive(:get_config).and_return(nil)
|
133
|
+
allow(Git).to receive(:push).and_return(nil)
|
134
|
+
allow(Git).to receive(:commit).and_return(nil)
|
135
|
+
allow(Git).to receive(:get_remote).and_return(nil)
|
136
|
+
allow(@pivotal_story).to receive(:story_type).and_return('feature')
|
137
|
+
allow(@pivotal_story).to receive(:id).and_return('123456')
|
138
|
+
allow(@pivotal_story).to receive(:name).and_return('test')
|
139
|
+
allow(@pivotal_story).to receive(:description).and_return('description')
|
143
140
|
@story = GithubPivotalFlow::Story.new(@project, @pivotal_story)
|
144
141
|
allow(@story).to receive(:ask).and_return('test')
|
145
142
|
end
|
146
143
|
|
147
144
|
it 'prompts the user for a branch extension name' do
|
148
145
|
allow(@story).to receive(:branch_prefix).and_return('feature/')
|
149
|
-
expect(@story).to receive(:ask).with("Enter branch name (feature
|
146
|
+
expect(@story).to receive(:ask).with("Enter branch name (feature/<branch-name>): ").and_return('super-branch')
|
150
147
|
|
151
148
|
@story.create_branch!('Message')
|
152
149
|
end
|
153
150
|
|
154
151
|
it 'includes a tag to skip the ci build for the initial blank commit' do
|
155
|
-
@story.
|
152
|
+
allow(@story).to receive(:branch_name).and_return('feature/123456-my_branch')
|
156
153
|
expect(Git).to receive(:commit).with(hash_including(commit_message: 'Message [ci skip]')).and_return(true)
|
157
154
|
|
158
155
|
@story.create_branch!('Message')
|
159
156
|
end
|
160
157
|
|
161
|
-
it '
|
162
|
-
@story.
|
163
|
-
expect(Git).
|
158
|
+
it 'does not push the local branch' do
|
159
|
+
allow(@story).to receive(:branch_name).and_return('feature/123456-my_branch')
|
160
|
+
expect(Git).to_not receive(:push)
|
164
161
|
|
165
162
|
@story.create_branch!('Message')
|
166
163
|
end
|
167
164
|
|
168
165
|
it 'supports stories with quotes in their name' do
|
169
|
-
@story.
|
166
|
+
allow(@story).to receive(:name).and_return('Fancy story with "quotes"')
|
167
|
+
allow(@story).to receive(:branch_name).and_return('feature/123456-my_branch')
|
170
168
|
expect(Git).to receive(:commit).with(hash_including(commit_message: 'Starting [feature #123456]: Fancy story with \"quotes\" [ci skip]'))
|
171
169
|
|
172
170
|
@story.create_branch!
|
metadata
CHANGED
@@ -1,139 +1,139 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: github-pivotal-flow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Donald Piret
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.6'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.6'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pivotal-tracker
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0.5'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0.5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '1.3'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '1.3'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - ~>
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '10.0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - ~>
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '10.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: redcarpet
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - ~>
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '2.2'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - ~>
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '2.2'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - ~>
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '2.14'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - ~>
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '2.14'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: rspec-mocks
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - ~>
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: '2.14'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - ~>
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '2.14'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: simplecov
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - ~>
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
117
|
version: '0.7'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - ~>
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0.7'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: yard
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - ~>
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0.8'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - ~>
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0.8'
|
139
139
|
description: Provides a set of additional Git commands to help developers when working
|
@@ -141,6 +141,7 @@ description: Provides a set of additional Git commands to help developers when w
|
|
141
141
|
email: donald@donaldpiret.com
|
142
142
|
executables:
|
143
143
|
- git-finish
|
144
|
+
- git-publish
|
144
145
|
- git-start
|
145
146
|
extensions: []
|
146
147
|
extra_rdoc_files: []
|
@@ -148,6 +149,7 @@ files:
|
|
148
149
|
- LICENSE
|
149
150
|
- README.md
|
150
151
|
- bin/git-finish
|
152
|
+
- bin/git-publish
|
151
153
|
- bin/git-start
|
152
154
|
- lib/core_ext/object/blank.rb
|
153
155
|
- lib/github_pivotal_flow.rb
|
@@ -158,6 +160,7 @@ files:
|
|
158
160
|
- lib/github_pivotal_flow/github_api.rb
|
159
161
|
- lib/github_pivotal_flow/prepare-commit-msg.sh
|
160
162
|
- lib/github_pivotal_flow/project.rb
|
163
|
+
- lib/github_pivotal_flow/publish.rb
|
161
164
|
- lib/github_pivotal_flow/shell.rb
|
162
165
|
- lib/github_pivotal_flow/start.rb
|
163
166
|
- lib/github_pivotal_flow/story.rb
|
@@ -166,6 +169,7 @@ files:
|
|
166
169
|
- spec/github_pivotal_flow/configuration_spec.rb
|
167
170
|
- spec/github_pivotal_flow/finish_spec.rb
|
168
171
|
- spec/github_pivotal_flow/git_spec.rb
|
172
|
+
- spec/github_pivotal_flow/publish_spec.rb
|
169
173
|
- spec/github_pivotal_flow/shell_spec.rb
|
170
174
|
- spec/github_pivotal_flow/start_spec.rb
|
171
175
|
- spec/github_pivotal_flow/story_spec.rb
|
@@ -179,17 +183,17 @@ require_paths:
|
|
179
183
|
- lib
|
180
184
|
required_ruby_version: !ruby/object:Gem::Requirement
|
181
185
|
requirements:
|
182
|
-
- -
|
186
|
+
- - ">="
|
183
187
|
- !ruby/object:Gem::Version
|
184
188
|
version: 1.9.3
|
185
189
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
186
190
|
requirements:
|
187
|
-
- -
|
191
|
+
- - ">="
|
188
192
|
- !ruby/object:Gem::Version
|
189
193
|
version: '0'
|
190
194
|
requirements: []
|
191
195
|
rubyforge_project:
|
192
|
-
rubygems_version: 2.2.
|
196
|
+
rubygems_version: 2.2.2
|
193
197
|
signing_key:
|
194
198
|
specification_version: 4
|
195
199
|
summary: Git commands for integration with Pivotal Tracker and Github pull requests
|
@@ -198,6 +202,7 @@ test_files:
|
|
198
202
|
- spec/github_pivotal_flow/configuration_spec.rb
|
199
203
|
- spec/github_pivotal_flow/finish_spec.rb
|
200
204
|
- spec/github_pivotal_flow/git_spec.rb
|
205
|
+
- spec/github_pivotal_flow/publish_spec.rb
|
201
206
|
- spec/github_pivotal_flow/shell_spec.rb
|
202
207
|
- spec/github_pivotal_flow/start_spec.rb
|
203
208
|
- spec/github_pivotal_flow/story_spec.rb
|