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.
@@ -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
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- assisted_workflow (0.2.1)
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.4.1)
16
- safe_yaml (~> 0.9.0)
16
+ crack (0.1.8)
17
17
  fakefs (0.5.0)
18
- faraday (0.8.8)
19
- multipart-post (~> 1.2.0)
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.0)
33
+ mime-types (2.1)
25
34
  mini_portile (0.5.2)
26
- multipart-post (1.2.0)
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.0)
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
- safe_yaml (0.9.7)
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 task, moving to a new git branch for the new feature/bug fix
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 pivotal task, removing both local and remote feature branches
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
@@ -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
 
@@ -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, "assisted_workflow/output"
6
+ autoload :Output, "assisted_workflow/output"
7
7
 
8
8
  module Addons
9
- autoload :Pivotal, "assisted_workflow/addons/pivotal"
10
- autoload :Git, "assisted_workflow/addons/git"
11
- autoload :Github, "assisted_workflow/addons/github"
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(repo, branch, story)
64
+ def create_pull_request(branch, story)
22
65
  log "submiting the new pull request"
23
66
  base = "master"
24
- title = "[##{story.id}] #{story.name}"
25
- pull_request = @client.create_pull_request(repo, base, branch, title, story.description)
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
- else
29
- url = pull_request._links.html.href
30
- log "new pull request at #{url}"
31
- url
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 = pivotal.find_story(story_id)
40
+ story = tracker.find_story(story_id)
41
41
  if story.nil?
42
- stories = pivotal.pending_stories(:include_started => options[:all])
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
- pivotal.start_story(story, :estimate => options[:estimate])
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 = pivotal.find_story(story_id)
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 pivotal
110
- @pivotal ||= Addons::Pivotal.new(out, configuration[:pivotal])
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, configuration[:github])
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,3 +1,3 @@
1
1
  module AssistedWorkflow
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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
- # stubs
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("flaviogranero/assisted_workflow", "master", "flavio.00001.new_feature", "[#00001] New Feature", "Feature description"){ 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
- "flaviogranero/assisted_workflow", "flavio.00001.new_feature", story
31
- ).must_match /flaviogranero\/assisted_workflow\/pull\/1/
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("flaviogranero/invalid_repo", "master", "flavio.00001.new_feature", "[#00001] New Feature", "Feature description"){ nil }
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
- "flaviogranero/invalid_repo", "flavio.00001.new_feature", story
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
- agent = Sawyer::Agent.new("", {:links_parser => Sawyer::LinkParsers::Simple.new})
51
- data = {_links: {html: {href: "https://github.com/flaviogranero/assisted_workflow/pull/1"}}}
52
- Sawyer::Resource.new(agent, data)
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 wrapper" do
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.2.1
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-14 00:00:00.000000000 Z
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: 4272922658484443950
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: 4272922658484443950
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