assisted_workflow 0.2.1 → 0.3.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.
- 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
|