assisted_workflow 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +54 -0
- data/Gemfile.lock +20 -10
- data/README.md +2 -2
- data/Rakefile +9 -0
- data/assisted_workflow.gemspec +1 -0
- data/lib/assisted_workflow.rb +14 -4
- data/lib/assisted_workflow/addons/github.rb +91 -8
- data/lib/assisted_workflow/addons/jira.rb +105 -0
- data/lib/assisted_workflow/cli.rb +18 -22
- data/lib/assisted_workflow/version.rb +1 -1
- data/spec/assisted_workflow/addons/github_spec.rb +71 -14
- data/spec/assisted_workflow/addons/jira_spec.rb +137 -0
- data/spec/assisted_workflow/addons/pivotal_spec.rb +1 -5
- metadata +24 -4
data/CHANGELOG.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
### Unreleased
|
2
|
+
|
3
|
+
### 0.3.0
|
4
|
+
|
5
|
+
* enchancements
|
6
|
+
* Adding `jira` support to be used as story tracker, replacing pivotal
|
7
|
+
* Adding `github` issues support as another story tracker option
|
8
|
+
|
9
|
+
### 0.2.1
|
10
|
+
|
11
|
+
* bug fix
|
12
|
+
* Removing duplicated git add-on
|
13
|
+
|
14
|
+
### 0.2.0
|
15
|
+
|
16
|
+
* enchancements
|
17
|
+
* Moving libs to the new addons namespace, with a base class
|
18
|
+
* Introducing an output helper to deal with shell prints
|
19
|
+
|
20
|
+
### 0.1.4
|
21
|
+
|
22
|
+
* bug fix
|
23
|
+
* Fixing a bug preventing git commands to be executed
|
24
|
+
|
25
|
+
### 0.1.3
|
26
|
+
|
27
|
+
* enchancements
|
28
|
+
* Tasks shortcuts
|
29
|
+
* Adding specs to libraries
|
30
|
+
* Setting up travis-ci
|
31
|
+
|
32
|
+
### 0.1.2
|
33
|
+
|
34
|
+
* enchancements
|
35
|
+
* pivotal: adding pull request link as a story note on submiting
|
36
|
+
* pivotal: showing story description
|
37
|
+
* start command: --all option to include started stories
|
38
|
+
|
39
|
+
* bug fix
|
40
|
+
* finish command: do not raise if remote branch does not exists
|
41
|
+
* setup command: chmod git commit-msg hook to allow execution
|
42
|
+
|
43
|
+
### 0.1.1
|
44
|
+
|
45
|
+
* enchancements
|
46
|
+
* renaming aka to aw (assisted_workflow)
|
47
|
+
|
48
|
+
### 0.1.0
|
49
|
+
|
50
|
+
* AssistedWorkflow::CLI
|
51
|
+
* AssistedWorkflow::Git
|
52
|
+
* AssistedWorkflow::Github
|
53
|
+
* AssistedWorkflow::Pivotal
|
54
|
+
* AssistedWorkflow::ConfigFile
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
assisted_workflow (0.
|
4
|
+
assisted_workflow (0.3.0)
|
5
5
|
hashie (~> 2.0.5)
|
6
|
+
jiralicious (~> 0.4.0)
|
6
7
|
octokit (~> 2.0)
|
7
8
|
pivotal-tracker (~> 0.5.12)
|
8
9
|
thor (~> 0.18.1)
|
@@ -12,23 +13,33 @@ GEM
|
|
12
13
|
specs:
|
13
14
|
addressable (2.3.5)
|
14
15
|
builder (3.2.2)
|
15
|
-
crack (0.
|
16
|
-
safe_yaml (~> 0.9.0)
|
16
|
+
crack (0.1.8)
|
17
17
|
fakefs (0.5.0)
|
18
|
-
faraday (0.
|
19
|
-
multipart-post (
|
18
|
+
faraday (0.9.0)
|
19
|
+
multipart-post (>= 1.2, < 3)
|
20
20
|
happymapper (0.4.1)
|
21
21
|
libxml-ruby (~> 2.0)
|
22
22
|
hashie (2.0.5)
|
23
|
+
httparty (0.11.0)
|
24
|
+
multi_json (~> 1.0)
|
25
|
+
multi_xml (>= 0.5.2)
|
26
|
+
jiralicious (0.4.0)
|
27
|
+
crack (~> 0.1.8)
|
28
|
+
hashie (>= 1.1)
|
29
|
+
httparty (>= 0.10, < 0.12.0)
|
30
|
+
json (>= 1.6, < 1.9.0)
|
31
|
+
json (1.8.1)
|
23
32
|
libxml-ruby (2.7.0)
|
24
|
-
mime-types (2.
|
33
|
+
mime-types (2.1)
|
25
34
|
mini_portile (0.5.2)
|
26
|
-
|
35
|
+
multi_json (1.8.4)
|
36
|
+
multi_xml (0.5.5)
|
37
|
+
multipart-post (2.0.0)
|
27
38
|
nokogiri (1.6.1)
|
28
39
|
mini_portile (~> 0.5.0)
|
29
40
|
nokogiri-happymapper (0.5.8)
|
30
41
|
nokogiri (~> 1.5)
|
31
|
-
octokit (2.7.
|
42
|
+
octokit (2.7.1)
|
32
43
|
sawyer (~> 0.5.2)
|
33
44
|
pivotal-tracker (0.5.12)
|
34
45
|
builder
|
@@ -44,8 +55,7 @@ GEM
|
|
44
55
|
rest-client (1.6.7)
|
45
56
|
mime-types (>= 1.16)
|
46
57
|
rr (1.1.2)
|
47
|
-
|
48
|
-
sawyer (0.5.2)
|
58
|
+
sawyer (0.5.3)
|
49
59
|
addressable (~> 2.3.5)
|
50
60
|
faraday (~> 0.8, < 0.10)
|
51
61
|
thor (0.18.1)
|
data/README.md
CHANGED
@@ -8,10 +8,10 @@ AW is a CLI tool to automate software development workflows based on github pull
|
|
8
8
|
|
9
9
|
Here in [Inaka](http://inaka.net) we have the following workflow steps:
|
10
10
|
|
11
|
-
1. Start a pivotal
|
11
|
+
1. Start a pivotal/jira/github story, moving to a new git branch for the new feature/bug fix
|
12
12
|
2. Commit the changes, pushing to a new remote branch
|
13
13
|
3. Submit a pull-request, allowing other team member to review the code, and merge into master if everything is ok
|
14
|
-
4. Finish the
|
14
|
+
4. Finish the story, removing both local and remote feature branches
|
15
15
|
5. Deploy master branch.
|
16
16
|
|
17
17
|
For more details, please read more about the [Inaka Workflow](https://github.com/inaka/inaka_corp/wiki/Inaka-Workflow).
|
data/Rakefile
CHANGED
@@ -4,4 +4,13 @@ require 'rake/testtask'
|
|
4
4
|
Rake::TestTask.new do |t|
|
5
5
|
t.libs << "spec"
|
6
6
|
t.pattern = "spec/**/*_spec.rb"
|
7
|
+
end
|
8
|
+
|
9
|
+
task :console do
|
10
|
+
require 'irb'
|
11
|
+
require 'irb/completion'
|
12
|
+
require 'assisted_workflow'
|
13
|
+
require 'assisted_workflow/cli'
|
14
|
+
ARGV.clear
|
15
|
+
IRB.start
|
7
16
|
end
|
data/assisted_workflow.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |gem|
|
|
24
24
|
|
25
25
|
gem.add_dependency "thor", "~> 0.18.1"
|
26
26
|
gem.add_dependency "pivotal-tracker", "~> 0.5.12"
|
27
|
+
gem.add_dependency "jiralicious", "~> 0.4.0"
|
27
28
|
gem.add_dependency "octokit", "~> 2.0"
|
28
29
|
gem.add_dependency "hashie", "~> 2.0.5"
|
29
30
|
|
data/lib/assisted_workflow.rb
CHANGED
@@ -3,11 +3,21 @@ require "assisted_workflow/exceptions"
|
|
3
3
|
|
4
4
|
module AssistedWorkflow
|
5
5
|
autoload :ConfigFile, "assisted_workflow/config_file"
|
6
|
-
autoload :Output,
|
6
|
+
autoload :Output, "assisted_workflow/output"
|
7
7
|
|
8
8
|
module Addons
|
9
|
-
autoload :Pivotal,
|
10
|
-
autoload :
|
11
|
-
autoload :
|
9
|
+
autoload :Pivotal, "assisted_workflow/addons/pivotal"
|
10
|
+
autoload :Jira, "assisted_workflow/addons/jira"
|
11
|
+
autoload :Git, "assisted_workflow/addons/git"
|
12
|
+
autoload :Github, "assisted_workflow/addons/github"
|
13
|
+
|
14
|
+
# based on configuration keys, load the tracker addon
|
15
|
+
def self.load_tracker(out, configuration)
|
16
|
+
if configuration[:jira] && configuration[:jira][:project]
|
17
|
+
Jira.new(out, configuration[:jira])
|
18
|
+
elsif configuration[:pivotal] && configuration[:pivotal][:project_id]
|
19
|
+
Pivotal.new(out, configuration[:pivotal])
|
20
|
+
end
|
21
|
+
end
|
12
22
|
end
|
13
23
|
end
|
@@ -4,12 +4,55 @@ require "octokit"
|
|
4
4
|
|
5
5
|
module AssistedWorkflow::Addons
|
6
6
|
|
7
|
+
class GithubStory < SimpleDelegator
|
8
|
+
def initialize(issue)
|
9
|
+
super
|
10
|
+
@issue = issue
|
11
|
+
end
|
12
|
+
|
13
|
+
def id
|
14
|
+
@issue.number
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
@issue.title
|
19
|
+
end
|
20
|
+
|
21
|
+
def description
|
22
|
+
@issue.body.to_s.gsub("\r\n", "\n")
|
23
|
+
end
|
24
|
+
|
25
|
+
def other_id
|
26
|
+
@issue.assignee.login
|
27
|
+
end
|
28
|
+
|
29
|
+
def current_state
|
30
|
+
other_id
|
31
|
+
end
|
32
|
+
|
33
|
+
def labels
|
34
|
+
@issue.labels.map(&:name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def estimate
|
38
|
+
labels.join(", ")
|
39
|
+
# @issue.repository.name if @issue.repository
|
40
|
+
end
|
41
|
+
|
42
|
+
def issue
|
43
|
+
@issue
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
7
47
|
class Github < Base
|
8
|
-
required_options :token
|
48
|
+
required_options :token, :repository
|
9
49
|
|
10
50
|
def initialize(output, options = {})
|
11
51
|
super
|
12
52
|
@client = Octokit::Client.new(:access_token => options["token"])
|
53
|
+
|
54
|
+
@repo = options["repository"]
|
55
|
+
@username = @client.user.login
|
13
56
|
end
|
14
57
|
|
15
58
|
# Creates a pull request using current branch changes
|
@@ -18,17 +61,57 @@ module AssistedWorkflow::Addons
|
|
18
61
|
# @param branch [String] Branch name. flavio.0001.new_feature
|
19
62
|
# @param story [Story] Pivotal story object
|
20
63
|
# @return [Sawyer::Resource] The newly created pull request
|
21
|
-
def create_pull_request(
|
64
|
+
def create_pull_request(branch, story)
|
22
65
|
log "submiting the new pull request"
|
23
66
|
base = "master"
|
24
|
-
|
25
|
-
|
67
|
+
pull_request = if story.is_a? GithubStory
|
68
|
+
@client.create_pull_request_for_issue(@repo, base, branch, story.id)
|
69
|
+
else
|
70
|
+
title = "[##{story.id}] #{story.name}"
|
71
|
+
@client.create_pull_request(@repo, base, branch, title, story.description)
|
72
|
+
end
|
73
|
+
|
26
74
|
if pull_request.nil?
|
27
75
|
raise AssistedWorkflow::Error, "error on submiting the pull request"
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
76
|
+
end
|
77
|
+
|
78
|
+
url = pull_request._links.html.href
|
79
|
+
log "new pull request at #{url}"
|
80
|
+
url
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_story(story_id)
|
84
|
+
if !story_id.nil?
|
85
|
+
log "loading story ##{story_id}"
|
86
|
+
issue = @client.issue(@repo, story_id)
|
87
|
+
story = GithubStory.new(issue) if issue
|
88
|
+
story
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def start_story(story, options = {})
|
93
|
+
log "starting story ##{story.id}"
|
94
|
+
current_labels = story.labels
|
95
|
+
current_labels.delete "finished"
|
96
|
+
current_labels.push "started"
|
97
|
+
@client.reopen_issue(@repo, story.id, :assignee => @username, :labels => current_labels)
|
98
|
+
end
|
99
|
+
|
100
|
+
def finish_story(story, options = {})
|
101
|
+
log "finishing story ##{story.id}"
|
102
|
+
current_labels = story.labels
|
103
|
+
current_labels.delete "started"
|
104
|
+
current_labels.push "finished"
|
105
|
+
@client.reopen_issue(@repo, story.id, :assignee => @username, :labels => current_labels)
|
106
|
+
end
|
107
|
+
|
108
|
+
def pending_stories(options = {})
|
109
|
+
log "loading pending stories"
|
110
|
+
opt = {:state => "open"}
|
111
|
+
opt[:assignee] = @username unless options[:include_started]
|
112
|
+
issues = @client.issues(@repo, opt)
|
113
|
+
issues.map do |issue|
|
114
|
+
GithubStory.new(issue)
|
32
115
|
end
|
33
116
|
end
|
34
117
|
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "assisted_workflow/exceptions"
|
2
|
+
require "assisted_workflow/addons/base"
|
3
|
+
require 'jiralicious'
|
4
|
+
|
5
|
+
# wrapper class to pivotal api client
|
6
|
+
module AssistedWorkflow::Addons
|
7
|
+
|
8
|
+
# adapter class to map jira issue attributes for the required interface
|
9
|
+
class JiraStory < SimpleDelegator
|
10
|
+
|
11
|
+
def initialize(issue)
|
12
|
+
super
|
13
|
+
@issue = issue
|
14
|
+
end
|
15
|
+
|
16
|
+
def id
|
17
|
+
@issue.jira_key
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
@issue.summary
|
22
|
+
end
|
23
|
+
|
24
|
+
def other_id
|
25
|
+
@issue.fields.current["assignee"]["name"].split.join
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_state
|
29
|
+
@issue.fields.current["status"]["name"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def estimate
|
33
|
+
@issue.fields.current["priority"]["name"]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Jira < Base
|
38
|
+
required_options :username, :password, :uri, :project, :unstarted, :started, :finished
|
39
|
+
|
40
|
+
def initialize(output, options = {})
|
41
|
+
super
|
42
|
+
Jiralicious.configure do |config|
|
43
|
+
config.username = options["username"]
|
44
|
+
config.password = options["password"]
|
45
|
+
config.uri = options["uri"]
|
46
|
+
config.api_version = "latest"
|
47
|
+
config.auth_type = :basic
|
48
|
+
end
|
49
|
+
@project = options["project"]
|
50
|
+
@username = options["username"]
|
51
|
+
@unstarted = options["unstarted"]
|
52
|
+
@started = options["started"]
|
53
|
+
@finished = options["finished"]
|
54
|
+
end
|
55
|
+
|
56
|
+
def find_story(story_id)
|
57
|
+
if !story_id.nil?
|
58
|
+
log "loading story ##{story_id}"
|
59
|
+
issue = Jiralicious::Issue.find(story_id)
|
60
|
+
story = JiraStory.new(issue) if issue
|
61
|
+
story
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def start_story(story, options = {})
|
66
|
+
log "starting story ##{story.id}"
|
67
|
+
move_story! story, @started
|
68
|
+
end
|
69
|
+
|
70
|
+
def finish_story(story, options = {})
|
71
|
+
log "finishing story ##{story.id}"
|
72
|
+
move_story! story, @finished
|
73
|
+
story.comments.add(options[:note]) if options[:note]
|
74
|
+
end
|
75
|
+
|
76
|
+
def pending_stories(options = {})
|
77
|
+
log "loading pending stories"
|
78
|
+
states = [@unstarted]
|
79
|
+
states << @started if options[:include_started]
|
80
|
+
query = "project=#{@project} and assignee='#{@username}' and status in ('#{states.join("','")}')"
|
81
|
+
response = Jiralicious.search(query, :max_results => 5)
|
82
|
+
log "loading stories info"
|
83
|
+
response.issues_raw.map do |issue|
|
84
|
+
JiraStory.new(Jiralicious::Issue.new(issue))
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def valid?
|
89
|
+
!@project.nil?
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def move_story!(story, status)
|
95
|
+
url = "#{Jiralicious.rest_path}/issue/#{story.id}/transitions"
|
96
|
+
transitions = Jiralicious::Issue.get_transitions(url)
|
97
|
+
transition = transitions.parsed_response["transitions"].find{|t| t["name"] == status}
|
98
|
+
if transition
|
99
|
+
Jiralicious::Issue.transition(url, {"transition" => transition["id"]})
|
100
|
+
else
|
101
|
+
raise AssistedWorkflow::Error, "cannot find a valid transation to move the story to #{status}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -37,13 +37,13 @@ module AssistedWorkflow
|
|
37
37
|
method_option :estimate, :type => :numeric, :aliases => "-e", :desc => "Sets the story estimate when starting"
|
38
38
|
def start(story_id=nil)
|
39
39
|
check_awfile!
|
40
|
-
story =
|
40
|
+
story = tracker.find_story(story_id)
|
41
41
|
if story.nil?
|
42
|
-
stories =
|
42
|
+
stories = tracker.pending_stories(:include_started => options[:all])
|
43
43
|
out.print_stories "pending stories", stories, options
|
44
44
|
out.next_command "start a story using:", "$ aw start [STORY_ID]"
|
45
45
|
else
|
46
|
-
|
46
|
+
tracker.start_story(story, :estimate => options[:estimate])
|
47
47
|
out.print_story story
|
48
48
|
git.create_story_branch(story)
|
49
49
|
out.next_command "after commiting your changes, submit a pull request using:", "$ aw submit"
|
@@ -54,30 +54,24 @@ module AssistedWorkflow
|
|
54
54
|
def submit
|
55
55
|
check_awfile!
|
56
56
|
story_id = git.current_story_id
|
57
|
-
story =
|
58
|
-
if story
|
59
|
-
git.rebase_and_push
|
60
|
-
pr_url = github.create_pull_request(
|
61
|
-
git.repository, git.current_branch, story
|
62
|
-
)
|
63
|
-
pivotal.finish_story(story, :note => pr_url)
|
64
|
-
out.next_command "after pull request approval, remove the feature branch using:", "$ aw finish"
|
65
|
-
else
|
57
|
+
unless story = tracker.find_story(story_id)
|
66
58
|
raise AssistedWorkflow::Error, "story not found, make sure a feature branch in active"
|
67
59
|
end
|
60
|
+
git.rebase_and_push
|
61
|
+
pr_url = github.create_pull_request(git.current_branch, story)
|
62
|
+
tracker.finish_story(story, :note => pr_url)
|
63
|
+
out.next_command "after pull request approval, remove the feature branch using:", "$ aw finish"
|
68
64
|
end
|
69
65
|
|
70
66
|
desc "finish", "Check if the changes are merged into master, removing the current feature branch"
|
71
67
|
def finish
|
72
68
|
check_awfile!
|
73
|
-
story_id = git.current_story_id
|
74
|
-
if story_id.to_i > 0
|
75
|
-
git.check_merged!
|
76
|
-
git.remove_branch
|
77
|
-
out.next_command "well done! check your next stories using:", "$ aw start"
|
78
|
-
else
|
69
|
+
unless story_id = git.current_story_id
|
79
70
|
raise AssistedWorkflow::Error, "story not found, make sure a feature branch in active"
|
80
71
|
end
|
72
|
+
git.check_merged!
|
73
|
+
git.remove_branch
|
74
|
+
out.next_command "well done! check your next stories using:", "$ aw start"
|
81
75
|
end
|
82
76
|
|
83
77
|
desc "version", "Display assisted_workflow gem version"
|
@@ -106,16 +100,18 @@ module AssistedWorkflow
|
|
106
100
|
@out ||= Output.new(self.shell)
|
107
101
|
end
|
108
102
|
|
109
|
-
def
|
110
|
-
@
|
103
|
+
def tracker
|
104
|
+
@tracker ||= Addons.load_tracker(out, configuration) || github
|
111
105
|
end
|
112
|
-
|
106
|
+
|
113
107
|
def git
|
114
108
|
@git ||= Addons::Git.new(out)
|
115
109
|
end
|
116
110
|
|
117
111
|
def github
|
118
|
-
@github ||= Addons::Github.new(out,
|
112
|
+
@github ||= Addons::Github.new(out,
|
113
|
+
{"repository" => git.repository}.merge(configuration[:github])
|
114
|
+
)
|
119
115
|
end
|
120
116
|
|
121
117
|
def config_file
|
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'pivotal_tracker'
|
2
3
|
require 'assisted_workflow/addons/github'
|
3
4
|
|
4
5
|
describe AssistedWorkflow::Addons::Github do
|
5
6
|
before do
|
6
7
|
@configuration = {
|
7
8
|
"token" => "mygithubtoken",
|
9
|
+
"repository" => "fakeuser/fakerepo"
|
8
10
|
}
|
9
|
-
|
10
|
-
@client = Object.new
|
11
|
-
stub(@client).user_authenticated?{ true }
|
11
|
+
@client = client_stub
|
12
12
|
stub(Octokit::Client).new{ @client }
|
13
13
|
|
14
14
|
@github = AssistedWorkflow::Addons::Github.new(nil, @configuration)
|
@@ -18,37 +18,94 @@ describe AssistedWorkflow::Addons::Github do
|
|
18
18
|
assert @github.valid?
|
19
19
|
end
|
20
20
|
|
21
|
-
it "requires token" do
|
21
|
+
it "requires token and repository configuration" do
|
22
22
|
proc {
|
23
23
|
AssistedWorkflow::Addons::Github.new(nil, {})
|
24
|
-
}.must_raise AssistedWorkflow::Error, "github missing configuration:[token]"
|
24
|
+
}.must_raise AssistedWorkflow::Error, "github missing configuration:[token,repository]"
|
25
25
|
end
|
26
26
|
|
27
|
-
it "creates a new valid pull request" do
|
28
|
-
mock(@client).create_pull_request("
|
27
|
+
it "creates a new valid pull request from a pivotal story" do
|
28
|
+
mock(@client).create_pull_request("fakeuser/fakerepo", "master", "fakeuser.00001.new_feature", "[#00001] New Feature", "Feature description"){ pull_request }
|
29
29
|
@github.create_pull_request(
|
30
|
-
"
|
31
|
-
).must_match /
|
30
|
+
"fakeuser.00001.new_feature", story
|
31
|
+
).must_match /fakeuser\/fakerepo\/pull\/1/
|
32
|
+
end
|
33
|
+
|
34
|
+
it "creates a new valid pull request from a github story" do
|
35
|
+
mock(@client).create_pull_request_for_issue("fakeuser/fakerepo", "master", "fakeuser.00001.new_feature", 10){ pull_request }
|
36
|
+
@github.create_pull_request(
|
37
|
+
"fakeuser.00001.new_feature",
|
38
|
+
AssistedWorkflow::Addons::GithubStory.new(gh_issue(:number => 10))
|
39
|
+
).must_match /fakeuser\/fakerepo\/pull\/1/
|
32
40
|
end
|
33
41
|
|
34
42
|
it "raises on creating an invalid pull request" do
|
35
|
-
mock(@client).create_pull_request("
|
43
|
+
mock(@client).create_pull_request("fakeuser/fakerepo", "master", "fakeuser.00001.new_feature", "[#00001] New Feature", "Feature description"){ nil }
|
36
44
|
proc {
|
37
45
|
@github.create_pull_request(
|
38
|
-
"
|
46
|
+
"fakeuser.00001.new_feature", story
|
39
47
|
)
|
40
48
|
}.must_raise AssistedWorkflow::Error, "error on submiting the pull request"
|
41
49
|
end
|
42
50
|
|
51
|
+
it "finds a story by id" do
|
52
|
+
mock(@client).issue(@configuration["repository"], "10") do |repo, issue_number|
|
53
|
+
gh_issue(:number => issue_number)
|
54
|
+
end
|
55
|
+
|
56
|
+
story = @github.find_story("10")
|
57
|
+
story.id.must_equal "10"
|
58
|
+
story.other_id.must_match /fakeuser/
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns pending stories" do
|
62
|
+
mock(@client).issues(@configuration["repository"], {
|
63
|
+
:state => "open", :assignee => "fakeuser"
|
64
|
+
}) do
|
65
|
+
[ gh_issue ]
|
66
|
+
end
|
67
|
+
|
68
|
+
stories = @github.pending_stories(:include_started => false)
|
69
|
+
stories.size.must_equal 1
|
70
|
+
end
|
71
|
+
|
72
|
+
it "starts a story" do
|
73
|
+
mock(@client).reopen_issue(@configuration["repository"], gh_issue.number, :assignee => "fakeuser", :labels => ["bug","started"]){ true }
|
74
|
+
@github.start_story(AssistedWorkflow::Addons::GithubStory.new(gh_issue))
|
75
|
+
end
|
76
|
+
|
77
|
+
it "finishes a story" do
|
78
|
+
mock(@client).reopen_issue(@configuration["repository"], gh_issue.number, :assignee => "fakeuser", :labels => ["bug","finished"]){ true }
|
79
|
+
@github.finish_story(AssistedWorkflow::Addons::GithubStory.new(gh_issue))
|
80
|
+
end
|
81
|
+
|
43
82
|
private #==================================================================
|
44
83
|
|
45
84
|
def story
|
46
85
|
@story ||= PivotalTracker::Story.new(:id => "00001", :name => "New Feature", :description => "Feature description")
|
47
86
|
end
|
48
87
|
|
88
|
+
def agent_stub
|
89
|
+
Sawyer::Agent.new("", {:links_parser => Sawyer::LinkParsers::Simple.new})
|
90
|
+
end
|
91
|
+
|
92
|
+
def gh_issue(attributes = {})
|
93
|
+
@gh_issue ||= Sawyer::Resource.new(agent_stub, attributes.merge({
|
94
|
+
:assignee => {:login => "fakeuser"},
|
95
|
+
:labels => [{:name => "bug"}]
|
96
|
+
}))
|
97
|
+
end
|
98
|
+
|
49
99
|
def pull_request
|
50
|
-
|
51
|
-
|
52
|
-
|
100
|
+
Sawyer::Resource.new(agent_stub, {_links: {html: {href: "https://github.com/fakeuser/fakerepo/pull/1"}}})
|
101
|
+
end
|
102
|
+
|
103
|
+
def client_stub
|
104
|
+
client = Object.new
|
105
|
+
user = Object.new
|
106
|
+
stub(user).login{ "fakeuser" }
|
107
|
+
stub(client).user{ user }
|
108
|
+
stub(client).user_authenticated?{ true }
|
109
|
+
client
|
53
110
|
end
|
54
111
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'assisted_workflow/addons/jira'
|
3
|
+
|
4
|
+
describe AssistedWorkflow::Addons::Jira do
|
5
|
+
|
6
|
+
before do
|
7
|
+
@configuration = {
|
8
|
+
"username" => "jirauser",
|
9
|
+
"password" => "jirapass",
|
10
|
+
"uri" => "aw.atlassian.net",
|
11
|
+
"project" => "AW",
|
12
|
+
"unstarted" => "Backlog",
|
13
|
+
"started" => "Started",
|
14
|
+
"finished" => "Finished"
|
15
|
+
}
|
16
|
+
@jira = AssistedWorkflow::Addons::Jira.new(nil, @configuration)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "initializes a valid jira addon" do
|
20
|
+
assert @jira.valid?
|
21
|
+
end
|
22
|
+
|
23
|
+
it "requires username" do
|
24
|
+
proc {
|
25
|
+
AssistedWorkflow::Addons::Jira.new(
|
26
|
+
nil,
|
27
|
+
@configuration.reject{|k,v| k == "username"}
|
28
|
+
)
|
29
|
+
}.must_raise AssistedWorkflow::Error, "jira missing configuration:[username]"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "requires password" do
|
33
|
+
proc {
|
34
|
+
AssistedWorkflow::Addons::Jira.new(
|
35
|
+
nil,
|
36
|
+
@configuration.reject{|k,v| k == "password"}
|
37
|
+
)
|
38
|
+
}.must_raise AssistedWorkflow::Error, "jira missing configuration:[password]"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "requires url" do
|
42
|
+
proc {
|
43
|
+
AssistedWorkflow::Addons::Jira.new(
|
44
|
+
nil,
|
45
|
+
@configuration.reject{|k,v| k == "uri"}
|
46
|
+
)
|
47
|
+
}.must_raise AssistedWorkflow::Error, "jira missing configuration:[url]"
|
48
|
+
end
|
49
|
+
|
50
|
+
it "requires project" do
|
51
|
+
proc {
|
52
|
+
AssistedWorkflow::Addons::Jira.new(
|
53
|
+
nil,
|
54
|
+
@configuration.reject{|k,v| k == "project"}
|
55
|
+
)
|
56
|
+
}.must_raise AssistedWorkflow::Error, "jira missing configuration:[project]"
|
57
|
+
end
|
58
|
+
|
59
|
+
it "requires statuses configuration" do
|
60
|
+
["unstarted", "started", "finished"].each do |status|
|
61
|
+
proc {
|
62
|
+
AssistedWorkflow::Addons::Jira.new(
|
63
|
+
nil,
|
64
|
+
@configuration.reject{|k,v| k == status}
|
65
|
+
)
|
66
|
+
}.must_raise AssistedWorkflow::Error, "jira missing configuration:[#{status}]"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
it "finds a story by id" do
|
71
|
+
mock(Jiralicious::Issue).find("aw-01") do |story_id|
|
72
|
+
story_stub(:jira_key => story_id)
|
73
|
+
end
|
74
|
+
|
75
|
+
story = @jira.find_story("aw-01")
|
76
|
+
story.id.must_equal "aw-01"
|
77
|
+
story.other_id.must_match /jirauser/
|
78
|
+
end
|
79
|
+
|
80
|
+
it "returns pending stories" do
|
81
|
+
query = "project=AW and assignee='jirauser' and status in ('Backlog','Started')"
|
82
|
+
mock(Jiralicious).search(query, :max_results => 5){ search_results_stub }
|
83
|
+
|
84
|
+
stories = @jira.pending_stories(:include_started => true)
|
85
|
+
stories.size.must_equal 2
|
86
|
+
end
|
87
|
+
|
88
|
+
it "starts a story" do
|
89
|
+
story = story_stub(:jira_key => "aw-01")
|
90
|
+
mock_transition(story.id, "2")
|
91
|
+
@jira.start_story(story)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "finishes a story" do
|
95
|
+
story = story_stub(:id => "aw-01")
|
96
|
+
any_instance_of(Jiralicious::Issue::Comment) do |klass|
|
97
|
+
stub(klass).add("pull_request_url"){ true }
|
98
|
+
end
|
99
|
+
mock_transition(story.id, "3")
|
100
|
+
assert @jira.finish_story(story, :note => "pull_request_url")
|
101
|
+
end
|
102
|
+
|
103
|
+
private #===================================================================
|
104
|
+
|
105
|
+
def story_stub(attributes = {})
|
106
|
+
stub(Jiralicious::Issue::Comment).find_by_key{ Jiralicious::Issue::Comment.new }
|
107
|
+
stub(Jiralicious::Issue::Watchers).find_by_key{ nil }
|
108
|
+
|
109
|
+
default = {
|
110
|
+
"fields" => {"assignee" => {"name" => "jirauser"}}
|
111
|
+
}
|
112
|
+
Jiralicious::Issue.new(default.merge(attributes))
|
113
|
+
end
|
114
|
+
|
115
|
+
def search_results_stub
|
116
|
+
search_data = {
|
117
|
+
"issues" => [story_stub(:jira_key => "aw-01"), story_stub(:jira_key => "aw-02")],
|
118
|
+
"offset" => 0,
|
119
|
+
"num_results" => 2
|
120
|
+
}
|
121
|
+
Jiralicious::SearchResult.new(search_data)
|
122
|
+
end
|
123
|
+
|
124
|
+
def mock_transition(story_id, status_id)
|
125
|
+
url = "#{Jiralicious.rest_path}/issue/#{story_id}/transitions"
|
126
|
+
response = Object.new
|
127
|
+
stub(response).parsed_response do
|
128
|
+
{"transitions" => [
|
129
|
+
{"id" => "1", "name" => "Backlog"},
|
130
|
+
{"id" => "2", "name" => "Started"},
|
131
|
+
{"id" => "3", "name" => "Finished"},
|
132
|
+
]}
|
133
|
+
end
|
134
|
+
mock(Jiralicious::Issue).get_transitions(url){ response }
|
135
|
+
mock(Jiralicious::Issue).transition(url, {"transition" => status_id})
|
136
|
+
end
|
137
|
+
end
|
@@ -17,7 +17,7 @@ describe AssistedWorkflow::Addons::Pivotal do
|
|
17
17
|
@pivotal = AssistedWorkflow::Addons::Pivotal.new(nil, @configuration)
|
18
18
|
end
|
19
19
|
|
20
|
-
it "initializes a valid pivotal
|
20
|
+
it "initializes a valid pivotal addon" do
|
21
21
|
assert @pivotal.valid?
|
22
22
|
end
|
23
23
|
|
@@ -89,10 +89,6 @@ describe AssistedWorkflow::Addons::Pivotal do
|
|
89
89
|
story.errors.must_be_empty
|
90
90
|
end
|
91
91
|
|
92
|
-
it "returns arrays to be printed" do
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
92
|
private #===================================================================
|
97
93
|
|
98
94
|
def story_stub(attributes = {})
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: assisted_workflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2014-02-
|
12
|
+
date: 2014-02-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -91,6 +91,22 @@ dependencies:
|
|
91
91
|
- - ~>
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: 0.5.12
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: jiralicious
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.4.0
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.4.0
|
94
110
|
- !ruby/object:Gem::Dependency
|
95
111
|
name: octokit
|
96
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +152,7 @@ extra_rdoc_files: []
|
|
136
152
|
files:
|
137
153
|
- .gitignore
|
138
154
|
- .travis.yml
|
155
|
+
- CHANGELOG.md
|
139
156
|
- Gemfile
|
140
157
|
- Gemfile.lock
|
141
158
|
- LICENSE.txt
|
@@ -147,6 +164,7 @@ files:
|
|
147
164
|
- lib/assisted_workflow/addons/base.rb
|
148
165
|
- lib/assisted_workflow/addons/git.rb
|
149
166
|
- lib/assisted_workflow/addons/github.rb
|
167
|
+
- lib/assisted_workflow/addons/jira.rb
|
150
168
|
- lib/assisted_workflow/addons/pivotal.rb
|
151
169
|
- lib/assisted_workflow/cli.rb
|
152
170
|
- lib/assisted_workflow/config_file.rb
|
@@ -158,6 +176,7 @@ files:
|
|
158
176
|
- lib/assisted_workflow/version.rb
|
159
177
|
- spec/assisted_workflow/addons/git_spec.rb
|
160
178
|
- spec/assisted_workflow/addons/github_spec.rb
|
179
|
+
- spec/assisted_workflow/addons/jira_spec.rb
|
161
180
|
- spec/assisted_workflow/addons/pivotal_spec.rb
|
162
181
|
- spec/assisted_workflow/config_file_spec.rb
|
163
182
|
- spec/spec_helper.rb
|
@@ -218,7 +237,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
218
237
|
version: '0'
|
219
238
|
segments:
|
220
239
|
- 0
|
221
|
-
hash:
|
240
|
+
hash: -4110324392104534794
|
222
241
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
223
242
|
none: false
|
224
243
|
requirements:
|
@@ -227,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
246
|
version: '0'
|
228
247
|
segments:
|
229
248
|
- 0
|
230
|
-
hash:
|
249
|
+
hash: -4110324392104534794
|
231
250
|
requirements: []
|
232
251
|
rubyforge_project:
|
233
252
|
rubygems_version: 1.8.24
|
@@ -238,6 +257,7 @@ summary: AW is a CLI tool to automate software development workflows based on gi
|
|
238
257
|
test_files:
|
239
258
|
- spec/assisted_workflow/addons/git_spec.rb
|
240
259
|
- spec/assisted_workflow/addons/github_spec.rb
|
260
|
+
- spec/assisted_workflow/addons/jira_spec.rb
|
241
261
|
- spec/assisted_workflow/addons/pivotal_spec.rb
|
242
262
|
- spec/assisted_workflow/config_file_spec.rb
|
243
263
|
- spec/spec_helper.rb
|