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.
@@ -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