livebuzz-gitx 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +31 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +84 -0
- data/Rakefile +5 -0
- data/bin/git-buildtag +6 -0
- data/bin/git-cleanup +7 -0
- data/bin/git-integrate +6 -0
- data/bin/git-nuke +6 -0
- data/bin/git-release +6 -0
- data/bin/git-review +6 -0
- data/bin/git-reviewrequest +8 -0
- data/bin/git-share +6 -0
- data/bin/git-start +6 -0
- data/bin/git-track +6 -0
- data/bin/git-update +6 -0
- data/lib/livebuzz/gitx/cli/base_command.rb +58 -0
- data/lib/livebuzz/gitx/cli/buildtag_command.rb +41 -0
- data/lib/livebuzz/gitx/cli/cleanup_command.rb +47 -0
- data/lib/livebuzz/gitx/cli/integrate_command.rb +95 -0
- data/lib/livebuzz/gitx/cli/nuke_command.rb +64 -0
- data/lib/livebuzz/gitx/cli/release_command.rb +41 -0
- data/lib/livebuzz/gitx/cli/review_command.rb +101 -0
- data/lib/livebuzz/gitx/cli/share_command.rb +17 -0
- data/lib/livebuzz/gitx/cli/start_command.rb +39 -0
- data/lib/livebuzz/gitx/cli/track_command.rb +16 -0
- data/lib/livebuzz/gitx/cli/update_command.rb +37 -0
- data/lib/livebuzz/gitx/configuration.rb +47 -0
- data/lib/livebuzz/gitx/extensions/string.rb +12 -0
- data/lib/livebuzz/gitx/extensions/thor.rb +39 -0
- data/lib/livebuzz/gitx/github.rb +177 -0
- data/lib/livebuzz/gitx/version.rb +5 -0
- data/lib/livebuzz/gitx.rb +10 -0
- data/livebuzz-gitx.gemspec +37 -0
- data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_failure_status.yml +135 -0
- data/spec/fixtures/vcr_cassettes/pull_request_does_exist_with_success_status.yml +149 -0
- data/spec/fixtures/vcr_cassettes/pull_request_does_not_exist.yml +133 -0
- data/spec/livebuzz/gitx/cli/base_command_spec.rb +41 -0
- data/spec/livebuzz/gitx/cli/buildtag_command_spec.rb +70 -0
- data/spec/livebuzz/gitx/cli/cleanup_command_spec.rb +37 -0
- data/spec/livebuzz/gitx/cli/integrate_command_spec.rb +272 -0
- data/spec/livebuzz/gitx/cli/nuke_command_spec.rb +165 -0
- data/spec/livebuzz/gitx/cli/release_command_spec.rb +148 -0
- data/spec/livebuzz/gitx/cli/review_command_spec.rb +307 -0
- data/spec/livebuzz/gitx/cli/share_command_spec.rb +32 -0
- data/spec/livebuzz/gitx/cli/start_command_spec.rb +96 -0
- data/spec/livebuzz/gitx/cli/track_command_spec.rb +31 -0
- data/spec/livebuzz/gitx/cli/update_command_spec.rb +79 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/support/global_config.rb +24 -0
- data/spec/support/home_env.rb +11 -0
- data/spec/support/matchers/meet_expectations_matcher.rb +7 -0
- data/spec/support/pry.rb +1 -0
- data/spec/support/stub_execution.rb +14 -0
- data/spec/support/timecop.rb +9 -0
- data/spec/support/vcr.rb +6 -0
- data/spec/support/webmock.rb +1 -0
- metadata +350 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'livebuzz/gitx'
|
3
|
+
require 'livebuzz/gitx/cli/base_command'
|
4
|
+
require 'livebuzz/gitx/cli/update_command'
|
5
|
+
require 'livebuzz/gitx/github'
|
6
|
+
|
7
|
+
module LiveBuzz
|
8
|
+
module Gitx
|
9
|
+
module Cli
|
10
|
+
class ReviewCommand < BaseCommand
|
11
|
+
include LiveBuzz::Gitx::Github
|
12
|
+
|
13
|
+
BUMP_COMMENT_PREFIX = '[gitx] review bump :tada:'
|
14
|
+
BUMP_COMMENT_FOOTER = <<-EOS.dedent
|
15
|
+
# Bump comments should include:
|
16
|
+
# * summary of what changed
|
17
|
+
#
|
18
|
+
# This footer will automatically be stripped from the created comment
|
19
|
+
EOS
|
20
|
+
APPROVAL_COMMENT_PREFIX = '[gitx] review approved :shipit:'
|
21
|
+
APPROVAL_COMMENT_FOOTER = <<-EOS.dedent
|
22
|
+
# Approval comments can include:
|
23
|
+
# * feedback
|
24
|
+
# * post-release follow-up items
|
25
|
+
#
|
26
|
+
# This footer will automatically be stripped from the created comment
|
27
|
+
EOS
|
28
|
+
REJECTION_COMMENT_PREFIX = '[gitx] review rejected'
|
29
|
+
REJECTION_COMMENT_FOOTER = <<-EOS.dedent
|
30
|
+
# Rejection comments should include:
|
31
|
+
# * feedback for fixes required before approved
|
32
|
+
#
|
33
|
+
# This footer will automatically be stripped from the created comment
|
34
|
+
EOS
|
35
|
+
|
36
|
+
desc "review", "Create or update a pull request on github"
|
37
|
+
method_option :description, :type => :string, :aliases => '-d', :desc => 'pull request description'
|
38
|
+
method_option :assignee, :type => :string, :aliases => '-a', :desc => 'pull request assignee'
|
39
|
+
method_option :open, :type => :boolean, :aliases => '-o', :desc => 'open the pull request in a web browser'
|
40
|
+
method_option :bump, :type => :boolean, :aliases => '-b', :desc => 'bump an existing pull request by posting a comment to re-review new changes'
|
41
|
+
method_option :approve, :type => :boolean, :desc => 'approve the pull request an post comment on pull request'
|
42
|
+
method_option :reject, :type => :boolean, :desc => 'reject the pull request an post comment on pull request'
|
43
|
+
# @see http://developer.github.com/v3/pulls/
|
44
|
+
def review
|
45
|
+
fail 'Github authorization token not found' unless authorization_token
|
46
|
+
|
47
|
+
branch = current_branch.name
|
48
|
+
pull_request = find_or_create_pull_request(branch)
|
49
|
+
bump_pull_request(pull_request) if options[:bump]
|
50
|
+
approve_pull_request(pull_request) if options[:approve]
|
51
|
+
reject_pull_request(pull_request) if options[:reject]
|
52
|
+
assign_pull_request(pull_request) if options[:assignee]
|
53
|
+
|
54
|
+
run_cmd "open #{pull_request.html_url}" if options[:open]
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def assign_pull_request(pull_request)
|
60
|
+
assignee = options[:assignee]
|
61
|
+
say "Assigning pull request to "
|
62
|
+
say assignee, :green
|
63
|
+
|
64
|
+
title = pull_request.title
|
65
|
+
body = pull_request.body
|
66
|
+
options = {
|
67
|
+
assignee: assignee
|
68
|
+
}
|
69
|
+
github_client.update_issue(github_slug, pull_request.number, title, body, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def bump_pull_request(pull_request)
|
73
|
+
comment = comment_from_template(pull_request, BUMP_COMMENT_PREFIX, BUMP_COMMENT_FOOTER)
|
74
|
+
set_review_status('pending', 'Peer review in progress')
|
75
|
+
end
|
76
|
+
|
77
|
+
def reject_pull_request(pull_request)
|
78
|
+
comment = comment_from_template(pull_request, REJECTION_COMMENT_PREFIX, REJECTION_COMMENT_FOOTER)
|
79
|
+
set_review_status('failure', 'Peer review rejected')
|
80
|
+
end
|
81
|
+
|
82
|
+
def approve_pull_request(pull_request)
|
83
|
+
comment = comment_from_template(pull_request, APPROVAL_COMMENT_PREFIX, APPROVAL_COMMENT_FOOTER)
|
84
|
+
set_review_status('success', 'Peer review approved')
|
85
|
+
end
|
86
|
+
|
87
|
+
def comment_from_template(pull_request, prefix, footer)
|
88
|
+
text = ask_editor("\n\n#{footer}", repo.config['core.editor'])
|
89
|
+
comment = [prefix, text].join("\n\n")
|
90
|
+
comment = comment.gsub(footer, '').chomp.strip
|
91
|
+
github_client.add_comment(github_slug, pull_request.number, comment)
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_review_status(state, description)
|
95
|
+
latest_commit = repo.head.target_id
|
96
|
+
update_review_status(latest_commit, state, description)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'livebuzz/gitx'
|
3
|
+
require 'livebuzz/gitx/cli/base_command'
|
4
|
+
|
5
|
+
module LiveBuzz
|
6
|
+
module Gitx
|
7
|
+
module Cli
|
8
|
+
class ShareCommand < BaseCommand
|
9
|
+
desc 'share', 'Share the current branch in the remote repository'
|
10
|
+
def share
|
11
|
+
run_cmd "git push origin #{current_branch.name}"
|
12
|
+
run_cmd "git branch --set-upstream-to origin/#{current_branch.name}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'livebuzz/gitx'
|
3
|
+
require 'livebuzz/gitx/cli/base_command'
|
4
|
+
|
5
|
+
module LiveBuzz
|
6
|
+
module Gitx
|
7
|
+
module Cli
|
8
|
+
class StartCommand < BaseCommand
|
9
|
+
EXAMPLE_BRANCH_NAMES = %w( api-fix-invalid-auth desktop-cleanup-avatar-markup share-form-add-edit-link )
|
10
|
+
VALID_BRANCH_NAME_REGEX = /^[A-Za-z0-9\-_]+$/
|
11
|
+
|
12
|
+
desc 'start', 'start a new git branch with latest changes from master'
|
13
|
+
def start(branch_name = nil)
|
14
|
+
until valid_new_branch_name?(branch_name)
|
15
|
+
branch_name = ask("What would you like to name your branch? (ex: #{EXAMPLE_BRANCH_NAMES.sample})")
|
16
|
+
end
|
17
|
+
|
18
|
+
checkout_branch LiveBuzz::Gitx::BASE_BRANCH
|
19
|
+
run_cmd 'git pull'
|
20
|
+
repo.create_branch branch_name, LiveBuzz::Gitx::BASE_BRANCH
|
21
|
+
checkout_branch branch_name
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def valid_new_branch_name?(branch)
|
27
|
+
return false if repo_branches.include?(branch)
|
28
|
+
branch =~ VALID_BRANCH_NAME_REGEX
|
29
|
+
end
|
30
|
+
|
31
|
+
def repo_branches
|
32
|
+
@branch_names ||= repo.branches.each_name.map do |branch|
|
33
|
+
branch.split('/').last
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'livebuzz/gitx'
|
3
|
+
require 'livebuzz/gitx/cli/base_command'
|
4
|
+
|
5
|
+
module LiveBuzz
|
6
|
+
module Gitx
|
7
|
+
module Cli
|
8
|
+
class TrackCommand < BaseCommand
|
9
|
+
desc 'track', 'set the current branch to track the remote branch with the same name'
|
10
|
+
def track
|
11
|
+
run_cmd "git branch --set-upstream-to origin/#{current_branch.name}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'livebuzz/gitx'
|
3
|
+
require 'livebuzz/gitx/cli/base_command'
|
4
|
+
|
5
|
+
module LiveBuzz
|
6
|
+
module Gitx
|
7
|
+
module Cli
|
8
|
+
class UpdateCommand < BaseCommand
|
9
|
+
desc 'update', 'Update the current branch with latest changes from the remote feature branch and master'
|
10
|
+
def update
|
11
|
+
say "Updating "
|
12
|
+
say "#{current_branch.name} ", :green
|
13
|
+
say "with latest changes from "
|
14
|
+
say LiveBuzz::Gitx::BASE_BRANCH, :green
|
15
|
+
|
16
|
+
update_branch(current_branch.name) if remote_branch_exists?(current_branch.name)
|
17
|
+
update_branch(LiveBuzz::Gitx::BASE_BRANCH)
|
18
|
+
run_cmd 'git push origin HEAD'
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def update_branch(branch)
|
24
|
+
begin
|
25
|
+
run_cmd "git pull origin #{branch}"
|
26
|
+
rescue
|
27
|
+
fail MergeError, "Merge Conflict Occurred. Please fix merge conflict and rerun the update command"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def remote_branch_exists?(branch)
|
32
|
+
repo.branches.each_name(:remote).include?("origin/#{branch}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module LiveBuzz
|
4
|
+
module Gitx
|
5
|
+
class Configuration
|
6
|
+
DEFAULT_CONFIG = {
|
7
|
+
'aggregate_branches' => %w( staging prototype ),
|
8
|
+
'reserved_branches' => %w( HEAD master next_release staging prototype ),
|
9
|
+
'taggable_branches' => %w( master staging )
|
10
|
+
}
|
11
|
+
CONFIG_FILE = '.gitx.yml'
|
12
|
+
|
13
|
+
attr_reader :config
|
14
|
+
|
15
|
+
def initialize(root_dir)
|
16
|
+
@config = Thor::CoreExt::HashWithIndifferentAccess.new(DEFAULT_CONFIG)
|
17
|
+
config_file_path = File.join(root_dir, CONFIG_FILE)
|
18
|
+
if File.exists?(config_file_path)
|
19
|
+
@config.merge!(::YAML::load_file(config_file_path))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def aggregate_branches
|
24
|
+
config[:aggregate_branches]
|
25
|
+
end
|
26
|
+
def aggregate_branch?(branch)
|
27
|
+
aggregate_branches.include?(branch)
|
28
|
+
end
|
29
|
+
|
30
|
+
def reserved_branches
|
31
|
+
config[:reserved_branches]
|
32
|
+
end
|
33
|
+
|
34
|
+
def reserved_branch?(branch)
|
35
|
+
reserved_branches.include?(branch)
|
36
|
+
end
|
37
|
+
|
38
|
+
def taggable_branches
|
39
|
+
config[:taggable_branches]
|
40
|
+
end
|
41
|
+
|
42
|
+
def taggable_branch?(branch)
|
43
|
+
taggable_branches.include?(branch)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class String
|
2
|
+
# @see http://api.rubyonrails.org/classes/String.html#method-i-strip_heredoc
|
3
|
+
def undent
|
4
|
+
indent = scan(/^[ \t]*(?=\S)/).min.size || 0
|
5
|
+
gsub(/^[ \t]{#{indent}}/, '')
|
6
|
+
end
|
7
|
+
alias :dedent :undent
|
8
|
+
|
9
|
+
def blank?
|
10
|
+
self.to_s == ''
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'English'
|
2
|
+
|
3
|
+
class Thor
|
4
|
+
module Actions
|
5
|
+
# execute a shell command and raise an error if non-zero exit code is returned
|
6
|
+
# return the string output from the command
|
7
|
+
def run_cmd(cmd, options = {})
|
8
|
+
say "$ #{cmd}"
|
9
|
+
output = `#{cmd}`
|
10
|
+
success = $CHILD_STATUS.to_i == 0
|
11
|
+
fail "#{cmd} failed" unless success || options[:allow_failure]
|
12
|
+
output
|
13
|
+
end
|
14
|
+
|
15
|
+
# launch configured editor to retreive message/string
|
16
|
+
# see http://osdir.com/ml/ruby-talk/2010-06/msg01424.html
|
17
|
+
# see https://gist.github.com/rkumar/456809
|
18
|
+
# see http://rdoc.info/github/visionmedia/commander/master/Commander/UI.ask_editor
|
19
|
+
def ask_editor(initial_text = '', editor = nil)
|
20
|
+
editor ||= ENV['EDITOR'] || 'vi'
|
21
|
+
Tempfile.open('comment.md') do |f|
|
22
|
+
f << initial_text
|
23
|
+
f.flush
|
24
|
+
|
25
|
+
flags = case editor
|
26
|
+
when 'mate', 'emacs', 'subl'
|
27
|
+
'-w'
|
28
|
+
when 'mvim'
|
29
|
+
'-f'
|
30
|
+
else
|
31
|
+
''
|
32
|
+
end
|
33
|
+
pid = fork { exec([editor, flags, f.path].join(' ')) }
|
34
|
+
Process.waitpid(pid)
|
35
|
+
File.read(f.path)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'octokit'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module LiveBuzz
|
6
|
+
module Gitx
|
7
|
+
module Github
|
8
|
+
GLOBAL_CONFIG_FILE = '~/.config/gitx/github.yml'
|
9
|
+
REVIEW_CONTEXT = 'peer_review'
|
10
|
+
CLIENT_URL = 'https://github.com/livebuzzevents/livebuzz-gitx'
|
11
|
+
PULL_REQUEST_FOOTER = <<-EOS.dedent
|
12
|
+
# Pull Request Protips(tm):
|
13
|
+
# * Include description of how this change accomplishes the task at hand.
|
14
|
+
# * Use GitHub flavored Markdown http://github.github.com/github-flavored-markdown/
|
15
|
+
# * Review CONTRIBUTING.md for recommendations of artifacts, links, images, screencasts, etc.
|
16
|
+
#
|
17
|
+
# This footer will automatically be stripped from the pull request description
|
18
|
+
EOS
|
19
|
+
|
20
|
+
def find_or_create_pull_request(branch)
|
21
|
+
pull_request = find_pull_request(branch)
|
22
|
+
pull_request ||= begin
|
23
|
+
execute_command(LiveBuzz::Gitx::Cli::UpdateCommand, :update)
|
24
|
+
pull_request = create_pull_request(branch)
|
25
|
+
say 'Created pull request: '
|
26
|
+
say pull_request.html_url, :green
|
27
|
+
|
28
|
+
pull_request
|
29
|
+
end
|
30
|
+
pull_request
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Sawyer::Resource] data structure of pull request info if found
|
34
|
+
# @return nil if no pull request found
|
35
|
+
def find_pull_request(branch)
|
36
|
+
head_reference = "#{github_organization}:#{branch}"
|
37
|
+
params = {
|
38
|
+
head: head_reference,
|
39
|
+
state: 'open'
|
40
|
+
}
|
41
|
+
pull_requests = github_client.pull_requests(github_slug, params)
|
42
|
+
pull_requests.first
|
43
|
+
end
|
44
|
+
|
45
|
+
# Get the current commit status of a branch
|
46
|
+
# @see https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref
|
47
|
+
def branch_status(branch)
|
48
|
+
response = github_client.status(github_slug, branch)
|
49
|
+
response.state
|
50
|
+
end
|
51
|
+
|
52
|
+
# Update build status with peer review status
|
53
|
+
def update_review_status(commit_sha, state, description)
|
54
|
+
github_client.create_status(github_slug, commit_sha, state, context: REVIEW_CONTEXT, description: description)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @see http://developer.github.com/v3/pulls/
|
58
|
+
def create_pull_request(branch)
|
59
|
+
say "Creating pull request for "
|
60
|
+
say "#{branch} ", :green
|
61
|
+
say "against "
|
62
|
+
say "#{LiveBuzz::Gitx::BASE_BRANCH} ", :green
|
63
|
+
say "in "
|
64
|
+
say github_slug, :green
|
65
|
+
|
66
|
+
title = branch
|
67
|
+
body = pull_request_body(branch)
|
68
|
+
github_client.create_pull_request(github_slug, LiveBuzz::Gitx::BASE_BRANCH, branch, title, body)
|
69
|
+
end
|
70
|
+
|
71
|
+
def pull_request_body(branch)
|
72
|
+
changelog = run_cmd("git log #{LiveBuzz::Gitx::BASE_BRANCH}...#{branch} --reverse --no-merges --pretty=format:'* %s%n%b'")
|
73
|
+
description = options[:description]
|
74
|
+
|
75
|
+
description_template = []
|
76
|
+
description_template << "#{description}\n" if description
|
77
|
+
description_template << '### Changelog'
|
78
|
+
description_template << changelog
|
79
|
+
description_template << PULL_REQUEST_FOOTER
|
80
|
+
|
81
|
+
body = ask_editor(description_template.join("\n"), repo.config['core.editor'])
|
82
|
+
body.gsub(PULL_REQUEST_FOOTER, '').chomp.strip
|
83
|
+
end
|
84
|
+
|
85
|
+
# authorization token used for github API calls
|
86
|
+
# the token is cached on the filesystem for future use
|
87
|
+
# @return [String] auth token stored in git (current repo, user config or installed global settings)
|
88
|
+
# @see http://developer.github.com/v3/oauth/#scopes
|
89
|
+
# @see http://developer.github.com/v3/#user-agent-required
|
90
|
+
def authorization_token
|
91
|
+
auth_token = global_config['token']
|
92
|
+
auth_token ||= begin
|
93
|
+
new_token = create_authorization
|
94
|
+
save_global_config('token' => new_token)
|
95
|
+
new_token
|
96
|
+
end
|
97
|
+
auth_token
|
98
|
+
end
|
99
|
+
|
100
|
+
def create_authorization
|
101
|
+
password = ask_without_echo("Github password for #{username}: ")
|
102
|
+
client = Octokit::Client.new(login: username, password: password)
|
103
|
+
options = {
|
104
|
+
:scopes => ['repo'],
|
105
|
+
:note => github_client_name,
|
106
|
+
:note_url => CLIENT_URL
|
107
|
+
}
|
108
|
+
two_factor_auth_token = ask_without_echo("Github two factor authorization token (if enabled): ")
|
109
|
+
options[:headers] = {'X-GitHub-OTP' => two_factor_auth_token} if two_factor_auth_token
|
110
|
+
response = client.create_authorization(options)
|
111
|
+
response.token
|
112
|
+
rescue Octokit::ClientError => e
|
113
|
+
say "Error creating authorization: #{e.message}", :red
|
114
|
+
retry
|
115
|
+
end
|
116
|
+
|
117
|
+
def github_client_name
|
118
|
+
timestamp = Time.now.utc.strftime('%FT%R:%S%z')
|
119
|
+
client_name = "The Garage Git eXtensions #{timestamp}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def github_client
|
123
|
+
@client ||= Octokit::Client.new(:access_token => authorization_token)
|
124
|
+
end
|
125
|
+
|
126
|
+
# @return [String] github username (ex: 'wireframe') of the current github.user
|
127
|
+
# @raise error if github.user is not configured
|
128
|
+
def username
|
129
|
+
username = repo.config['github.user']
|
130
|
+
fail "Github user not configured. Run: `git config --global github.user 'me@email.com'`" unless username
|
131
|
+
username
|
132
|
+
end
|
133
|
+
|
134
|
+
# @return the github slug for the current repository's remote origin url.
|
135
|
+
# @example
|
136
|
+
# git@github.com:socialcast/livebuzz/gitx.git #=> livebuzz/gitx
|
137
|
+
# @example
|
138
|
+
# https://github.com/socialcast/livebuzz/gitx.git #=> livebuzz/gitx
|
139
|
+
def github_slug
|
140
|
+
remote = repo.config['remote.origin.url']
|
141
|
+
remote.to_s.gsub(/\.git$/,'').split(/[:\/]/).last(2).join('/')
|
142
|
+
end
|
143
|
+
|
144
|
+
def github_organization
|
145
|
+
github_slug.split('/').first
|
146
|
+
end
|
147
|
+
|
148
|
+
def global_config_file
|
149
|
+
File.expand_path(GLOBAL_CONFIG_FILE)
|
150
|
+
end
|
151
|
+
|
152
|
+
def global_config
|
153
|
+
@config ||= begin
|
154
|
+
File.exists?(global_config_file) ? YAML.load_file(global_config_file) : {}
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def save_global_config(options)
|
159
|
+
config_dir = File.dirname(global_config_file)
|
160
|
+
::FileUtils.mkdir_p(config_dir, mode: 0700) unless File.exists?(config_dir)
|
161
|
+
|
162
|
+
@config = global_config.merge(options)
|
163
|
+
File.open(global_config_file, "a+") do |file|
|
164
|
+
file.truncate(0)
|
165
|
+
file.write(@config.to_yaml)
|
166
|
+
end
|
167
|
+
File.chmod(0600, global_config_file)
|
168
|
+
end
|
169
|
+
|
170
|
+
def ask_without_echo(message)
|
171
|
+
value = ask(message, echo: false)
|
172
|
+
say ''
|
173
|
+
value
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'livebuzz/gitx/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "livebuzz-gitx"
|
8
|
+
spec.version = LiveBuzz::Gitx::VERSION
|
9
|
+
spec.authors = ["LiveBuzz, forked from code by Ryan Sonnek"]
|
10
|
+
spec.email = ["rob.lambell@livebuzz.co.uk"]
|
11
|
+
spec.description = %q{Git eXtensions for common development workflow}
|
12
|
+
spec.summary = %q{Utility scripts for Git to increase productivity for common operations}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "rugged", '~> 0.21.0'
|
22
|
+
spec.add_runtime_dependency "thor"
|
23
|
+
spec.add_runtime_dependency "octokit"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec", '~> 3.0'
|
28
|
+
spec.add_development_dependency "pry", '>= 0'
|
29
|
+
spec.add_development_dependency "webmock", '>= 0'
|
30
|
+
spec.add_development_dependency "timecop", "~> 0.7.0"
|
31
|
+
spec.add_development_dependency "vcr"
|
32
|
+
spec.add_development_dependency "guard"
|
33
|
+
spec.add_development_dependency "guard-rspec"
|
34
|
+
spec.add_development_dependency "coveralls"
|
35
|
+
spec.add_development_dependency 'terminal-notifier'
|
36
|
+
spec.add_development_dependency 'terminal-notifier-guard'
|
37
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.github.com/repos/livebuzzevents/livebuzz-gitx/pulls?head=livebuzz:feature-branch&state=open
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
Accept:
|
11
|
+
- application/vnd.github.v3+json
|
12
|
+
User-Agent:
|
13
|
+
- Octokit Ruby Gem 3.4.0
|
14
|
+
Content-Type:
|
15
|
+
- application/json
|
16
|
+
Authorization:
|
17
|
+
- token 123123
|
18
|
+
Accept-Encoding:
|
19
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
20
|
+
response:
|
21
|
+
status:
|
22
|
+
code: 200
|
23
|
+
message: OK
|
24
|
+
headers:
|
25
|
+
Server:
|
26
|
+
- GitHub.com
|
27
|
+
Date:
|
28
|
+
- Wed, 10 Dec 2014 19:14:11 GMT
|
29
|
+
Content-Type:
|
30
|
+
- application/json; charset=utf-8
|
31
|
+
Status:
|
32
|
+
- 200 OK
|
33
|
+
X-Github-Media-Type:
|
34
|
+
- github.v3; format=json
|
35
|
+
X-Ratelimit-Limit:
|
36
|
+
- '60'
|
37
|
+
X-Ratelimit-Remaining:
|
38
|
+
- '58'
|
39
|
+
X-Ratelimit-Reset:
|
40
|
+
- '1418242282'
|
41
|
+
X-Xss-Protection:
|
42
|
+
- 1; mode=block
|
43
|
+
X-Frame-Options:
|
44
|
+
- deny
|
45
|
+
Content-Security-Policy:
|
46
|
+
- default-src 'none'
|
47
|
+
Content-Length:
|
48
|
+
- '83'
|
49
|
+
Access-Control-Allow-Credentials:
|
50
|
+
- 'true'
|
51
|
+
Access-Control-Expose-Headers:
|
52
|
+
- ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
|
53
|
+
X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
|
54
|
+
Access-Control-Allow-Origin:
|
55
|
+
- "*"
|
56
|
+
X-Github-Request-Id:
|
57
|
+
- C6CBAFAF:141F:450BDAC:54889B83
|
58
|
+
Strict-Transport-Security:
|
59
|
+
- max-age=31536000; includeSubdomains; preload
|
60
|
+
X-Content-Type-Options:
|
61
|
+
- nosniff
|
62
|
+
body:
|
63
|
+
encoding: UTF-8
|
64
|
+
string: '[{"html_url":"https://path/to/html/pull/request","issue_url":"https://api/path/to/issue/url","number":10,"head":{"ref":"branch_name"}}]'
|
65
|
+
http_version:
|
66
|
+
recorded_at: Wed, 10 Dec 2014 19:14:11 GMT
|
67
|
+
- request:
|
68
|
+
method: get
|
69
|
+
uri: https://api.github.com/repos/livebuzzevents/livebuzz-gitx/commits/feature-branch/status
|
70
|
+
body:
|
71
|
+
encoding: US-ASCII
|
72
|
+
string: ''
|
73
|
+
headers:
|
74
|
+
Accept:
|
75
|
+
- application/vnd.github.v3+json
|
76
|
+
User-Agent:
|
77
|
+
- Octokit Ruby Gem 3.4.0
|
78
|
+
Content-Type:
|
79
|
+
- application/json
|
80
|
+
Authorization:
|
81
|
+
- token 123123
|
82
|
+
Accept-Encoding:
|
83
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
84
|
+
response:
|
85
|
+
status:
|
86
|
+
code: 200
|
87
|
+
message: OK
|
88
|
+
headers:
|
89
|
+
Server:
|
90
|
+
- GitHub.com
|
91
|
+
Date:
|
92
|
+
- Wed, 10 Dec 2014 19:01:27 GMT
|
93
|
+
Content-Type:
|
94
|
+
- application/json; charset=utf-8
|
95
|
+
Transfer-Encoding:
|
96
|
+
- chunked
|
97
|
+
Status:
|
98
|
+
- 200 OK
|
99
|
+
X-Ratelimit-Limit:
|
100
|
+
- '5000'
|
101
|
+
X-Ratelimit-Remaining:
|
102
|
+
- '4993'
|
103
|
+
X-Ratelimit-Reset:
|
104
|
+
- '1418241620'
|
105
|
+
X-Oauth-Scopes:
|
106
|
+
- repo
|
107
|
+
X-Accepted-Oauth-Scopes:
|
108
|
+
- repo, repo:status
|
109
|
+
X-Github-Media-Type:
|
110
|
+
- github.v3; format=json
|
111
|
+
X-Xss-Protection:
|
112
|
+
- 1; mode=block
|
113
|
+
X-Frame-Options:
|
114
|
+
- deny
|
115
|
+
Content-Security-Policy:
|
116
|
+
- default-src 'none'
|
117
|
+
Access-Control-Allow-Credentials:
|
118
|
+
- 'true'
|
119
|
+
Access-Control-Expose-Headers:
|
120
|
+
- ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset,
|
121
|
+
X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval
|
122
|
+
Access-Control-Allow-Origin:
|
123
|
+
- "*"
|
124
|
+
X-Github-Request-Id:
|
125
|
+
- C6CBAFAF:141D:2F27072:54889887
|
126
|
+
Strict-Transport-Security:
|
127
|
+
- max-age=31536000; includeSubdomains; preload
|
128
|
+
X-Content-Type-Options:
|
129
|
+
- nosniff
|
130
|
+
body:
|
131
|
+
encoding: UTF-8
|
132
|
+
string: '{"state":"failure"}'
|
133
|
+
http_version:
|
134
|
+
recorded_at: Wed, 10 Dec 2014 19:14:11 GMT
|
135
|
+
recorded_with: VCR 2.9.2
|