octopolo 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +21 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/Guardfile +5 -0
- data/MIT-LICENSE +20 -0
- data/README.markdown +55 -0
- data/Rakefile +38 -0
- data/bash_completion.sh +13 -0
- data/bin/octopolo +21 -0
- data/bin/op +21 -0
- data/lib/octopolo.rb +15 -0
- data/lib/octopolo/changelog.rb +27 -0
- data/lib/octopolo/cli.rb +210 -0
- data/lib/octopolo/commands/accept_pull.rb +8 -0
- data/lib/octopolo/commands/compare_release.rb +9 -0
- data/lib/octopolo/commands/deployable.rb +8 -0
- data/lib/octopolo/commands/github_auth.rb +5 -0
- data/lib/octopolo/commands/new_branch.rb +9 -0
- data/lib/octopolo/commands/new_deployable.rb +8 -0
- data/lib/octopolo/commands/new_staging.rb +8 -0
- data/lib/octopolo/commands/octopolo_setup.rb +5 -0
- data/lib/octopolo/commands/pivotal_auth.rb +5 -0
- data/lib/octopolo/commands/pull_request.rb +13 -0
- data/lib/octopolo/commands/signoff.rb +10 -0
- data/lib/octopolo/commands/stage_up.rb +8 -0
- data/lib/octopolo/commands/stale_branches.rb +11 -0
- data/lib/octopolo/commands/sync_branch.rb +11 -0
- data/lib/octopolo/commands/tag_release.rb +13 -0
- data/lib/octopolo/config.rb +146 -0
- data/lib/octopolo/convenience_wrappers.rb +46 -0
- data/lib/octopolo/dated_branch_creator.rb +81 -0
- data/lib/octopolo/git.rb +262 -0
- data/lib/octopolo/github.rb +95 -0
- data/lib/octopolo/github/commit.rb +45 -0
- data/lib/octopolo/github/pull_request.rb +126 -0
- data/lib/octopolo/github/pull_request_creator.rb +127 -0
- data/lib/octopolo/github/user.rb +40 -0
- data/lib/octopolo/jira/story_commenter.rb +26 -0
- data/lib/octopolo/pivotal.rb +44 -0
- data/lib/octopolo/pivotal/story_commenter.rb +19 -0
- data/lib/octopolo/pull_request_merger.rb +99 -0
- data/lib/octopolo/renderer.rb +37 -0
- data/lib/octopolo/reports.rb +18 -0
- data/lib/octopolo/scripts.rb +23 -0
- data/lib/octopolo/scripts/accept_pull.rb +67 -0
- data/lib/octopolo/scripts/compare_release.rb +52 -0
- data/lib/octopolo/scripts/deployable.rb +27 -0
- data/lib/octopolo/scripts/github_auth.rb +87 -0
- data/lib/octopolo/scripts/new_branch.rb +34 -0
- data/lib/octopolo/scripts/new_deployable.rb +14 -0
- data/lib/octopolo/scripts/new_staging.rb +15 -0
- data/lib/octopolo/scripts/octopolo_setup.rb +55 -0
- data/lib/octopolo/scripts/pivotal_auth.rb +44 -0
- data/lib/octopolo/scripts/pull_request.rb +127 -0
- data/lib/octopolo/scripts/signoff.rb +85 -0
- data/lib/octopolo/scripts/stage_up.rb +26 -0
- data/lib/octopolo/scripts/stale_branches.rb +54 -0
- data/lib/octopolo/scripts/sync_branch.rb +37 -0
- data/lib/octopolo/scripts/tag_release.rb +70 -0
- data/lib/octopolo/templates/pull_request_body.erb +24 -0
- data/lib/octopolo/user_config.rb +112 -0
- data/lib/octopolo/version.rb +3 -0
- data/lib/octopolo/week.rb +130 -0
- data/octopolo.gemspec +31 -0
- data/spec/.DS_Store +0 -0
- data/spec/octopolo/cli_spec.rb +310 -0
- data/spec/octopolo/config_spec.rb +344 -0
- data/spec/octopolo/convenience_wrappers_spec.rb +80 -0
- data/spec/octopolo/dated_branch_creator_spec.rb +143 -0
- data/spec/octopolo/git_spec.rb +419 -0
- data/spec/octopolo/github/commit_spec.rb +59 -0
- data/spec/octopolo/github/pull_request_creator_spec.rb +174 -0
- data/spec/octopolo/github/pull_request_spec.rb +291 -0
- data/spec/octopolo/github/user_spec.rb +65 -0
- data/spec/octopolo/github_spec.rb +169 -0
- data/spec/octopolo/jira/stor_commenter_spec.rb +30 -0
- data/spec/octopolo/pivotal/story_commenter_spec.rb +34 -0
- data/spec/octopolo/pivotal_spec.rb +61 -0
- data/spec/octopolo/pull_request_merger_spec.rb +144 -0
- data/spec/octopolo/renderer_spec.rb +35 -0
- data/spec/octopolo/scripts/accept_pull_spec.rb +76 -0
- data/spec/octopolo/scripts/compare_release_spec.rb +115 -0
- data/spec/octopolo/scripts/deployable_spec.rb +52 -0
- data/spec/octopolo/scripts/github_auth_spec.rb +156 -0
- data/spec/octopolo/scripts/new_branch_spec.rb +41 -0
- data/spec/octopolo/scripts/new_deployable_spec.rb +18 -0
- data/spec/octopolo/scripts/new_staging_spec.rb +18 -0
- data/spec/octopolo/scripts/octopolo_setup_spec.rb +120 -0
- data/spec/octopolo/scripts/pivotal_auth_spec.rb +77 -0
- data/spec/octopolo/scripts/pull_request_spec.rb +217 -0
- data/spec/octopolo/scripts/signoff_spec.rb +139 -0
- data/spec/octopolo/scripts/stage_up_spec.rb +52 -0
- data/spec/octopolo/scripts/stale_branches_spec.rb +81 -0
- data/spec/octopolo/scripts/sync_branch_spec.rb +57 -0
- data/spec/octopolo/scripts/tag_release_spec.rb +108 -0
- data/spec/octopolo/user_config_spec.rb +167 -0
- data/spec/octopolo_spec.rb +7 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/engine_yard.cache +0 -0
- data/spec/support/sample_octopolo.yml +2 -0
- data/spec/support/sample_user.yml +2 -0
- data/templates/lib.erb +23 -0
- data/templates/script.erb +7 -0
- data/templates/spec.erb +29 -0
- 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,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
|
+
|