octopolo 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +21 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +3 -0
  7. data/Guardfile +5 -0
  8. data/MIT-LICENSE +20 -0
  9. data/README.markdown +55 -0
  10. data/Rakefile +38 -0
  11. data/bash_completion.sh +13 -0
  12. data/bin/octopolo +21 -0
  13. data/bin/op +21 -0
  14. data/lib/octopolo.rb +15 -0
  15. data/lib/octopolo/changelog.rb +27 -0
  16. data/lib/octopolo/cli.rb +210 -0
  17. data/lib/octopolo/commands/accept_pull.rb +8 -0
  18. data/lib/octopolo/commands/compare_release.rb +9 -0
  19. data/lib/octopolo/commands/deployable.rb +8 -0
  20. data/lib/octopolo/commands/github_auth.rb +5 -0
  21. data/lib/octopolo/commands/new_branch.rb +9 -0
  22. data/lib/octopolo/commands/new_deployable.rb +8 -0
  23. data/lib/octopolo/commands/new_staging.rb +8 -0
  24. data/lib/octopolo/commands/octopolo_setup.rb +5 -0
  25. data/lib/octopolo/commands/pivotal_auth.rb +5 -0
  26. data/lib/octopolo/commands/pull_request.rb +13 -0
  27. data/lib/octopolo/commands/signoff.rb +10 -0
  28. data/lib/octopolo/commands/stage_up.rb +8 -0
  29. data/lib/octopolo/commands/stale_branches.rb +11 -0
  30. data/lib/octopolo/commands/sync_branch.rb +11 -0
  31. data/lib/octopolo/commands/tag_release.rb +13 -0
  32. data/lib/octopolo/config.rb +146 -0
  33. data/lib/octopolo/convenience_wrappers.rb +46 -0
  34. data/lib/octopolo/dated_branch_creator.rb +81 -0
  35. data/lib/octopolo/git.rb +262 -0
  36. data/lib/octopolo/github.rb +95 -0
  37. data/lib/octopolo/github/commit.rb +45 -0
  38. data/lib/octopolo/github/pull_request.rb +126 -0
  39. data/lib/octopolo/github/pull_request_creator.rb +127 -0
  40. data/lib/octopolo/github/user.rb +40 -0
  41. data/lib/octopolo/jira/story_commenter.rb +26 -0
  42. data/lib/octopolo/pivotal.rb +44 -0
  43. data/lib/octopolo/pivotal/story_commenter.rb +19 -0
  44. data/lib/octopolo/pull_request_merger.rb +99 -0
  45. data/lib/octopolo/renderer.rb +37 -0
  46. data/lib/octopolo/reports.rb +18 -0
  47. data/lib/octopolo/scripts.rb +23 -0
  48. data/lib/octopolo/scripts/accept_pull.rb +67 -0
  49. data/lib/octopolo/scripts/compare_release.rb +52 -0
  50. data/lib/octopolo/scripts/deployable.rb +27 -0
  51. data/lib/octopolo/scripts/github_auth.rb +87 -0
  52. data/lib/octopolo/scripts/new_branch.rb +34 -0
  53. data/lib/octopolo/scripts/new_deployable.rb +14 -0
  54. data/lib/octopolo/scripts/new_staging.rb +15 -0
  55. data/lib/octopolo/scripts/octopolo_setup.rb +55 -0
  56. data/lib/octopolo/scripts/pivotal_auth.rb +44 -0
  57. data/lib/octopolo/scripts/pull_request.rb +127 -0
  58. data/lib/octopolo/scripts/signoff.rb +85 -0
  59. data/lib/octopolo/scripts/stage_up.rb +26 -0
  60. data/lib/octopolo/scripts/stale_branches.rb +54 -0
  61. data/lib/octopolo/scripts/sync_branch.rb +37 -0
  62. data/lib/octopolo/scripts/tag_release.rb +70 -0
  63. data/lib/octopolo/templates/pull_request_body.erb +24 -0
  64. data/lib/octopolo/user_config.rb +112 -0
  65. data/lib/octopolo/version.rb +3 -0
  66. data/lib/octopolo/week.rb +130 -0
  67. data/octopolo.gemspec +31 -0
  68. data/spec/.DS_Store +0 -0
  69. data/spec/octopolo/cli_spec.rb +310 -0
  70. data/spec/octopolo/config_spec.rb +344 -0
  71. data/spec/octopolo/convenience_wrappers_spec.rb +80 -0
  72. data/spec/octopolo/dated_branch_creator_spec.rb +143 -0
  73. data/spec/octopolo/git_spec.rb +419 -0
  74. data/spec/octopolo/github/commit_spec.rb +59 -0
  75. data/spec/octopolo/github/pull_request_creator_spec.rb +174 -0
  76. data/spec/octopolo/github/pull_request_spec.rb +291 -0
  77. data/spec/octopolo/github/user_spec.rb +65 -0
  78. data/spec/octopolo/github_spec.rb +169 -0
  79. data/spec/octopolo/jira/stor_commenter_spec.rb +30 -0
  80. data/spec/octopolo/pivotal/story_commenter_spec.rb +34 -0
  81. data/spec/octopolo/pivotal_spec.rb +61 -0
  82. data/spec/octopolo/pull_request_merger_spec.rb +144 -0
  83. data/spec/octopolo/renderer_spec.rb +35 -0
  84. data/spec/octopolo/scripts/accept_pull_spec.rb +76 -0
  85. data/spec/octopolo/scripts/compare_release_spec.rb +115 -0
  86. data/spec/octopolo/scripts/deployable_spec.rb +52 -0
  87. data/spec/octopolo/scripts/github_auth_spec.rb +156 -0
  88. data/spec/octopolo/scripts/new_branch_spec.rb +41 -0
  89. data/spec/octopolo/scripts/new_deployable_spec.rb +18 -0
  90. data/spec/octopolo/scripts/new_staging_spec.rb +18 -0
  91. data/spec/octopolo/scripts/octopolo_setup_spec.rb +120 -0
  92. data/spec/octopolo/scripts/pivotal_auth_spec.rb +77 -0
  93. data/spec/octopolo/scripts/pull_request_spec.rb +217 -0
  94. data/spec/octopolo/scripts/signoff_spec.rb +139 -0
  95. data/spec/octopolo/scripts/stage_up_spec.rb +52 -0
  96. data/spec/octopolo/scripts/stale_branches_spec.rb +81 -0
  97. data/spec/octopolo/scripts/sync_branch_spec.rb +57 -0
  98. data/spec/octopolo/scripts/tag_release_spec.rb +108 -0
  99. data/spec/octopolo/user_config_spec.rb +167 -0
  100. data/spec/octopolo_spec.rb +7 -0
  101. data/spec/spec_helper.rb +29 -0
  102. data/spec/support/engine_yard.cache +0 -0
  103. data/spec/support/sample_octopolo.yml +2 -0
  104. data/spec/support/sample_user.yml +2 -0
  105. data/templates/lib.erb +23 -0
  106. data/templates/script.erb +7 -0
  107. data/templates/spec.erb +29 -0
  108. metadata +344 -0
@@ -0,0 +1,34 @@
1
+ require_relative "../scripts"
2
+ require_relative "../git"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class NewBranch
7
+ include ConfigWrapper
8
+ include GitWrapper
9
+
10
+ attr_accessor :new_branch_name
11
+ attr_accessor :source_branch_name
12
+
13
+ def self.execute(new_branch_name=nil, source_branch_name=nil)
14
+ new(new_branch_name, source_branch_name).execute
15
+ end
16
+
17
+ def initialize(new_branch_name=nil, source_branch_name=nil)
18
+ @new_branch_name = new_branch_name
19
+ @source_branch_name = source_branch_name || config.deploy_branch
20
+ end
21
+
22
+ # Public: Perform the script
23
+ def execute
24
+ raise ArgumentError unless new_branch_name
25
+ git.new_branch(new_branch_name, source_branch_name)
26
+ end
27
+
28
+ # Public: Provide a default value if none is given
29
+ def default_source_branch_name
30
+ config.deploy_branch
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "../scripts"
2
+ require_relative "../dated_branch_creator"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class NewDeployable
7
+
8
+ def execute
9
+ DatedBranchCreator.perform Git::DEPLOYABLE_PREFIX
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,15 @@
1
+ require_relative "../scripts"
2
+ require_relative "../dated_branch_creator"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class NewStaging
7
+ include CLIWrapper
8
+
9
+ def execute
10
+ DatedBranchCreator.perform Git::STAGING_PREFIX
11
+ end
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,55 @@
1
+ require_relative "../github"
2
+ require_relative "../scripts"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class OctopoloSetup
7
+ include Base
8
+ extend UserConfigWrapper
9
+
10
+ def self.invoke(*args)
11
+ verify_git_extras_setup
12
+ verify_user_setup
13
+ end
14
+
15
+ def self.verify_git_extras_setup
16
+ install_git_extras unless git_extras_installed?
17
+ end
18
+
19
+ def self.git_extras_installed?
20
+ check = cli.perform "which git-extras", false
21
+ check.include? "git-extras"
22
+ end
23
+
24
+ def self.install_git_extras
25
+ cli.say "Updating Homebrew to ensure latest git-extras formula."
26
+ cli.perform "brew update"
27
+ cli.say "Installing git-extras"
28
+ cli.perform "brew install git-extras"
29
+ end
30
+
31
+ def self.verify_user_setup
32
+ verify_user_full_name
33
+ verify_user_github_credentials
34
+ end
35
+
36
+ def self.verify_user_full_name
37
+ # if it's not set, it uses the USER environment variable
38
+ if user_config.full_name == ENV["USER"]
39
+ name = cli.prompt "Your full name:"
40
+ user_config.full_name = name
41
+ else
42
+ cli.say "Full name '#{user_config.full_name}' already configured."
43
+ end
44
+ end
45
+
46
+ def self.verify_user_github_credentials
47
+ GitHub.check_connection
48
+ cli.say "Successfully configured API token."
49
+ rescue GitHub::BadCredentials, GitHub::TryAgain => e
50
+ # if any error occurs, generate a new token
51
+ cli.say e.message
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "../scripts"
2
+ require_relative "../pivotal"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class PivotalAuth
7
+ include UserConfigWrapper
8
+ include CLIWrapper
9
+
10
+ attr_accessor :email
11
+ attr_accessor :password
12
+ attr_accessor :token
13
+
14
+ def execute
15
+ ask_credentials
16
+ request_token
17
+ store_token
18
+ rescue Pivotal::BadCredentials => e
19
+ cli.say e.message
20
+ end
21
+
22
+ # Private: Ask the user for their credentials
23
+ def ask_credentials
24
+ self.email = cli.prompt "Your Pivotal Tracker email: "
25
+ self.password = cli.prompt_secret "Your Pivotal Tracker password (never stored): "
26
+ end
27
+ private :ask_credentials
28
+
29
+ # Private: Fetch the user's token from the Pivotal API
30
+ def request_token
31
+ self.token = Pivotal::Client.fetch_token(email, password)
32
+ end
33
+ private :request_token
34
+
35
+ # Private: Store the returned token from the Pivotal API
36
+ def store_token
37
+ user_config.set :pivotal_token, token
38
+ end
39
+ private :store_token
40
+ end
41
+ end
42
+ end
43
+
44
+ # vim: set ft=ruby: #
@@ -0,0 +1,127 @@
1
+ require_relative "../scripts"
2
+ require_relative "../github/pull_request"
3
+ require_relative "../pivotal/story_commenter"
4
+ require_relative "../jira/story_commenter"
5
+
6
+ module Octopolo
7
+ module Scripts
8
+ class PullRequest
9
+ include CLIWrapper
10
+ include ConfigWrapper
11
+ include GitWrapper
12
+
13
+ attr_accessor :title
14
+ attr_accessor :pull_request
15
+ attr_accessor :pivotal_ids
16
+ attr_accessor :jira_ids
17
+ attr_accessor :destination_branch
18
+
19
+ def self.execute(destination_branch=nil)
20
+ new(destination_branch).execute
21
+ end
22
+
23
+ def initialize(destination_branch=nil)
24
+ @destination_branch = destination_branch || default_destination_branch
25
+ end
26
+
27
+ def default_destination_branch
28
+ config.deploy_branch
29
+ end
30
+
31
+ def execute
32
+ GitHub.connect do
33
+ ask_questionaire
34
+ create_pull_request
35
+ update_pivotal
36
+ update_jira
37
+ open_pull_request
38
+ end
39
+ end
40
+
41
+ # Private: Ask questions to create a pull request
42
+ def ask_questionaire
43
+ alert_reserved_and_exit if git.reserved_branch?
44
+ announce
45
+ ask_title
46
+ ask_pivotal_ids if config.use_pivotal_tracker
47
+ ask_jira_ids if config.use_jira
48
+ end
49
+ private :ask_questionaire
50
+
51
+ # Private: Announce to the user the branches the pull request will reference
52
+ def announce
53
+ cli.say "Preparing a pull request for #{config.github_repo}/#{git.current_branch} to #{config.github_repo}/#{destination_branch}."
54
+ end
55
+ private :announce
56
+
57
+ # Private: Announces that the current branch is reserved and exits with a fail status
58
+ def alert_reserved_and_exit
59
+ cli.say "The current branch #{config.github_repo}/#{git.current_branch} is a reserved branch and can not have pull requests."
60
+ exit 1
61
+ end
62
+ private :alert_reserved_and_exit
63
+
64
+ # Private: Ask for a title for the pull request
65
+ def ask_title
66
+ self.title = cli.prompt "Title:"
67
+ end
68
+ private :ask_title
69
+
70
+ # Private: Ask for a Pivotal Tracker story IDs
71
+ def ask_pivotal_ids
72
+ self.pivotal_ids = cli.prompt("Pivotal Tracker story ID(s):").split(/[\s,]+/)
73
+ end
74
+ private :ask_pivotal_ids
75
+
76
+ # Private: Ask for a Pivotal Tracker story IDs
77
+ def ask_jira_ids
78
+ self.jira_ids = cli.prompt("Jira story ID(s):").split(/[\s,]+/)
79
+ end
80
+ private :ask_pivotal_ids
81
+
82
+ # Private: Create the pull request
83
+ #
84
+ # Returns a GitHub::PullRequest object
85
+ def create_pull_request
86
+ self.pull_request = GitHub::PullRequest.create config.github_repo, pull_request_attributes
87
+ end
88
+ private :create_pull_request
89
+
90
+ # Private: The attributes to send to create the pull request
91
+ #
92
+ # Returns a Hash
93
+ def pull_request_attributes
94
+ {
95
+ title: title,
96
+ destination_branch: destination_branch,
97
+ source_branch: git.current_branch,
98
+ pivotal_ids: pivotal_ids,
99
+ jira_ids: jira_ids,
100
+ }
101
+ end
102
+ private :pull_request_attributes
103
+
104
+ # Private: Handle the newly created pull request
105
+ def open_pull_request
106
+ cli.copy_to_clipboard pull_request.url
107
+ cli.open pull_request.url
108
+ end
109
+ private :open_pull_request
110
+
111
+ def update_pivotal
112
+ pivotal_ids.each do |story_id|
113
+ Pivotal::StoryCommenter.new(story_id, pull_request.url).perform
114
+ end if pivotal_ids
115
+ end
116
+ private :update_pivotal
117
+
118
+ def update_jira
119
+ jira_ids.each do |story_id|
120
+ Jira::StoryCommenter.new(story_id, pull_request.url).perform
121
+ end if jira_ids
122
+ end
123
+ private :update_jira
124
+
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,85 @@
1
+ require_relative "../scripts"
2
+ require_relative "../github/pull_request"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class Signoff
7
+ include ConfigWrapper
8
+ include CLIWrapper
9
+
10
+ attr_accessor :pull_request_id
11
+ attr_accessor :pull_request
12
+ attr_accessor :signoff_type
13
+
14
+ TYPES = [
15
+ "code review only",
16
+ "QA only",
17
+ "both code review and QA",
18
+ ]
19
+
20
+ def self.execute(pull_request_id=nil)
21
+ new(pull_request_id).execute
22
+ end
23
+
24
+ def initialize(pull_request_id=nil)
25
+ @pull_request_id = pull_request_id
26
+ @pull_request_id ||= cli.prompt("Pull Request ID: ")
27
+ end
28
+
29
+ def execute
30
+ preamble
31
+ ask_signoff_type
32
+ write_comment
33
+ open_pull_request
34
+ rescue WrongChoice
35
+ retry
36
+ end
37
+
38
+ # Private: Display information about the pull request
39
+ def preamble
40
+ cli.say %Q(Please review "#{pull_request.title}":)
41
+ cli.say pull_request.url
42
+ cli.spacer_line
43
+ end
44
+ private :preamble
45
+
46
+ # Private: Ask which type of signoff to perform
47
+ def ask_signoff_type
48
+ self.signoff_type = cli.ask "Which type of signoff are you performing?", TYPES
49
+ end
50
+ private :ask_signoff_type
51
+
52
+ # Private: Find the pull request to be signed off on
53
+ #
54
+ # Returns a GitHub::PullRequest
55
+ def pull_request
56
+ @pull_request ||= GitHub::PullRequest.new config.github_repo, Integer(pull_request_id)
57
+ end
58
+ private :pull_request
59
+ private :pull_request=
60
+
61
+ # Private: The body of the comment for the given signoff
62
+ #
63
+ # Returns a String
64
+ def comment_body
65
+ "Signing off on **#{signoff_type}**."
66
+ end
67
+ private :comment_body
68
+
69
+ # Private: Submit a comment to the pull request
70
+ def write_comment
71
+ pull_request.write_comment comment_body
72
+ end
73
+ private :write_comment
74
+
75
+ # Private: Open the pull request in the browser
76
+ def open_pull_request
77
+ cli.open pull_request.url
78
+ end
79
+
80
+ WrongChoice = Class.new StandardError
81
+ end
82
+ end
83
+ end
84
+
85
+ # vim: set ft=ruby: #
@@ -0,0 +1,26 @@
1
+ require_relative "../scripts"
2
+ require_relative "../pull_request_merger"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class StageUp
7
+ include CLIWrapper
8
+
9
+ attr_accessor :pull_request_id
10
+
11
+ def self.execute(pull_request_id=nil)
12
+ new(pull_request_id).execute
13
+ end
14
+
15
+ def initialize(pull_request_id=nil)
16
+ @pull_request_id = pull_request_id
17
+ end
18
+
19
+ # Public: Perform the script
20
+ def execute
21
+ self.pull_request_id ||= cli.prompt("Pull Request ID: ")
22
+ PullRequestMerger.perform Git::STAGING_PREFIX, Integer(pull_request_id)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,54 @@
1
+ require_relative "../git"
2
+ require_relative "../scripts"
3
+
4
+ module Octopolo
5
+ module Scripts
6
+ class StaleBranches
7
+ include CLIWrapper
8
+ include ConfigWrapper
9
+ include GitWrapper
10
+
11
+ attr_accessor :delete
12
+ alias_method :delete?, :delete
13
+
14
+ DEFAULT_BRANCHES = %W(HEAD master staging production)
15
+
16
+ def initialize(delete=false)
17
+ @delete = delete
18
+ end
19
+
20
+ def execute
21
+ if delete?
22
+ delete_stale_branches
23
+ else
24
+ display_stale_branches
25
+ end
26
+ end
27
+
28
+ # Private: Display the stale branches in the project
29
+ def display_stale_branches
30
+ stale_branches.each do |branch_name|
31
+ cli.say "* #{branch_name}"
32
+ end
33
+ end
34
+ private :display_stale_branches
35
+
36
+ # Private: Delete the stale branches in the project
37
+ def delete_stale_branches
38
+ stale_branches.each do |branch_name|
39
+ git.delete_branch(branch_name)
40
+ end
41
+ end
42
+ private :delete_stale_branches
43
+
44
+ # Private: The list of stale branches for the project
45
+ #
46
+ # Returns an Array of Strings
47
+ def stale_branches
48
+ git.stale_branches(config.deploy_branch, config.branches_to_keep)
49
+ end
50
+ private :stale_branches
51
+ end
52
+ end
53
+ end
54
+