bpescatore-stash 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,153 @@
1
+ require 'json'
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'git'
5
+ require 'launchy'
6
+
7
+ include Atlassian::Util::TextUtil
8
+
9
+ module Atlassian
10
+ module Stash
11
+ class CreatePullRequestResource
12
+ attr_accessor :resource
13
+
14
+ def initialize(sourceRepoInfo, targetRepoInfo, title, description, reviewers, source, target)
15
+ src_repository = {
16
+ 'slug' => sourceRepoInfo.slug,
17
+ 'project' => {
18
+ 'key' => sourceRepoInfo.projectKey
19
+ }
20
+ }
21
+ target_repository = {
22
+ 'slug' => targetRepoInfo.slug,
23
+ 'project' => {
24
+ 'key' => targetRepoInfo.projectKey
25
+ }
26
+ }
27
+ fromRef = {
28
+ 'id' => source,
29
+ 'repository' => src_repository
30
+ }
31
+ toRef = {
32
+ 'id' => target,
33
+ 'repository' => target_repository
34
+ }
35
+ @resource = {
36
+ 'title' => title,
37
+ 'fromRef' => fromRef,
38
+ 'toRef' => toRef
39
+ }
40
+
41
+ @resource["description"] = description unless description.empty?
42
+
43
+ @resource["reviewers"] = reviewers.collect { |r|
44
+ {
45
+ 'user' => {
46
+ 'name' => r
47
+ }
48
+ }
49
+ } unless reviewers.empty?
50
+ end
51
+ end
52
+
53
+ class CreatePullRequest
54
+
55
+ def initialize(config)
56
+ @config = config
57
+ end
58
+
59
+ def create_pull_request(source, target, reviewers, options)
60
+ Process.exit if not target or not source
61
+
62
+ @source = source
63
+ @target = target
64
+
65
+ srcRepoInfo = RepoInfo.create(@config, options.src_remote)
66
+ targetRepoInfo = RepoInfo.create(@config, options.target_remote)
67
+
68
+ title, description = title_and_description(options)
69
+
70
+ resource = CreatePullRequestResource.new(srcRepoInfo, targetRepoInfo, title, description, reviewers, @source, @target).resource
71
+
72
+ username = @config["username"]
73
+ password = @config["password"]
74
+ proxy_addr, proxy_port = parse_proxy(@config["proxy"])
75
+
76
+ username = ask("Username: ") unless @config["username"]
77
+ password = ask("Password: ") { |q| q.echo = '*' } unless @config["password"]
78
+
79
+ uri = URI.parse(@config["stash_url"])
80
+ prPath = targetRepoInfo.repoPath + '/pull-requests'
81
+
82
+ req = Net::HTTP::Post.new(uri.query.nil? ? "#{prPath}" : "#{prPath}?#{uri.query}", {'Content-Type' => 'application/json', 'Accept' => 'application/json'})
83
+ req.basic_auth username, password
84
+ req.body = resource.to_json
85
+ http = Net::HTTP.new(uri.host, uri.port, proxy_addr, proxy_port)
86
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @config["ssl_no_verify"]
87
+ http.use_ssl = uri.scheme.eql?("https")
88
+
89
+ response = http.start {|conn| conn.request(req) }
90
+
91
+ if not response.is_a? Net::HTTPCreated
92
+ responseBody = JSON.parse(response.body)
93
+ if responseBody['errors']
94
+ responseBody['errors'].collect { |error|
95
+ puts error['message']
96
+ if error['reviewerErrors']
97
+ error['reviewerErrors'].collect { |revError|
98
+ puts revError['message']
99
+ }
100
+ end
101
+ }
102
+ elsif responseBody['message']
103
+ puts responseBody['message']
104
+ else
105
+ puts 'An unknown error occurred.'
106
+ puts response.code
107
+ puts response.body
108
+ end
109
+ else
110
+ responseBody = JSON.parse(response.body)
111
+ prUri = uri.clone
112
+ prUri.path = prPath + '/' + responseBody['id'].to_s
113
+ prUri.query = uri.query
114
+ puts prUri.to_s
115
+
116
+ if @config["open"] || options.open
117
+ Launchy.open prUri.to_s
118
+ end
119
+ end
120
+ end
121
+
122
+ private
123
+
124
+ def title_from_branch
125
+ convert_branch_name_to_sentence(@source) || "Merge '#{@source}' into '#{@target}'"
126
+ end
127
+
128
+ def git_commit_messages
129
+ @commit_messages ||= `git log --reverse --format=%s #{@target}..#{@source}`
130
+ end
131
+
132
+ def parse_proxy(conf)
133
+ return nil, nil unless conf
134
+
135
+ addr, port = conf.split(":")
136
+ if port =~ /\d+/
137
+ port = port.to_i
138
+ else
139
+ port = nil
140
+ end
141
+ [addr, port]
142
+ end
143
+
144
+ def title_and_description(options)
145
+ descr = (options.description or git_commit_messages)
146
+ title = (options.title or title_from_branch)
147
+
148
+ [title, descr]
149
+ end
150
+ end
151
+ end
152
+ end
153
+
@@ -0,0 +1,62 @@
1
+ require 'git'
2
+
3
+ module Atlassian
4
+ module Stash
5
+ class RepoInfo
6
+ def initialize(config, projectKey, slug)
7
+ @config = config
8
+ @projectKey = projectKey
9
+ @slug = slug
10
+ end
11
+
12
+ def projectKey
13
+ @projectKey
14
+ end
15
+
16
+ def slug
17
+ @slug
18
+ end
19
+
20
+ def repoPath
21
+ uri = URI.parse(@config["stash_url"])
22
+ repoPath = uri.path + '/projects/' + @projectKey + '/repos/' + @slug
23
+ repoPath
24
+ end
25
+
26
+ def repoUrl(suffix, branch)
27
+ uri = URI.parse(@config["stash_url"])
28
+ path = repoPath + (suffix.nil? ? '' : '/' + suffix)
29
+ uri.path = path
30
+
31
+ if (!branch.nil? and !branch.empty?)
32
+ q = uri.query || ''
33
+ q = q + (q.empty? ? '' : '&') + 'at=' + branch unless branch.nil?
34
+ uri.query = q
35
+ end
36
+
37
+ uri.to_s
38
+ end
39
+
40
+ def self.create (config, remote=nil)
41
+ config = Hash.new if config.nil?
42
+ remote = config["remote"] if (remote.nil? || remote.empty?)
43
+ remoteUrl = Atlassian::Stash::Git.get_remote_url(remote)
44
+
45
+ if remoteUrl.nil?
46
+ remotes = Atlassian::Stash::Git.get_remotes
47
+ if remotes.empty?
48
+ raise "No git remotes found, could not determine Stash project URL"
49
+ else
50
+ remote = Atlassian::Stash::Git::DEFAULT_REMOTE if (remote.nil? || remote.empty?)
51
+ raise "Could not find requested git remote '#{remote}'. Remotes found: \r\n" + remotes
52
+ end
53
+ end
54
+
55
+ if m = remoteUrl.match(/\/([a-zA-Z~][a-zA-Z0-9_\-]*)\/([[:alnum:]][\w\-\.]*).git$/)
56
+ return RepoInfo.new(config, m[1], m[2])
57
+ end
58
+ raise "Repository does not seem to be hosted in Stash; Remote url: " + remoteUrl
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module Atlassian
3
+ module Stash
4
+ module Version
5
+ STRING = IO.readlines(File.dirname(__FILE__) + "/../../../VERSION").first.strip
6
+ end
7
+ end
8
+ end
9
+
@@ -0,0 +1,31 @@
1
+
2
+ module Atlassian
3
+ module Util
4
+ module TextUtil
5
+ def convert_branch_name_to_sentence(branch_name)
6
+ return '' if branch_name.nil?
7
+
8
+ branch_name = branch_name.to_s
9
+ return '' if branch_name.empty?
10
+
11
+ issue_key_regex = /([A-Z]{1,10}-\d+)/
12
+ branch_components = branch_name.split(issue_key_regex);
13
+
14
+ parts = branch_components.each_with_index.map { |value, index|
15
+ (index % 2 === 0) ? value.gsub(/[\-_]/, ' ') : value
16
+ }
17
+
18
+ to_sentence_case(parts.join(''))
19
+ end
20
+
21
+ def to_sentence_case(str)
22
+ return '' if str.nil?
23
+
24
+ str = str.to_s
25
+ return '' if str.empty?
26
+
27
+ str.slice(0, 1).upcase + str.slice(1, str.length)
28
+ end
29
+ end
30
+ end
31
+ end
data/lib/stash_cli.rb ADDED
@@ -0,0 +1,3 @@
1
+
2
+ Dir[File.join(File.dirname(__FILE__), "atlassian", "util", "*.rb")].sort.each {|f| require f}
3
+ Dir[File.join(File.dirname(__FILE__), "atlassian", "stash", "*.rb")].sort.each {|f| require f}
data/test/helper.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ if ENV["COVERAGE"]
5
+ require "simplecov"
6
+ SimpleCov.start :test_frameworks do
7
+ add_filter "/vendor/"
8
+ end
9
+ end
10
+
11
+ begin
12
+ Bundler.setup(:default, :development)
13
+ rescue Bundler::BundlerError => e
14
+ $stderr.puts e.message
15
+ $stderr.puts "Run `bundle install` to install missing gems"
16
+ exit e.status_code
17
+ end
18
+ require 'minitest/autorun'
19
+ require 'shoulda'
20
+ require "mocha/mini_test"
21
+
22
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
23
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
24
+ # require 'atlassian-stash'
25
+ require File.dirname(__FILE__) + "/../lib/stash_cli"
26
+
27
+
28
+ class Minitest::Test
29
+ end
@@ -0,0 +1,84 @@
1
+ require 'helper'
2
+
3
+ include Atlassian::Stash
4
+ include Atlassian::Stash::Git
5
+
6
+ class TestStashCreatePullRequest < Minitest::Test
7
+
8
+ context "#parse_proxy" do
9
+ setup do
10
+ @cpr = CreatePullRequest.new nil
11
+ end
12
+
13
+ context 'when proxy_conf is nil' do
14
+ should 'returns [nil, nil]' do
15
+ assert_equal [nil, nil], @cpr.send(:parse_proxy, nil)
16
+ end
17
+ end
18
+
19
+ context 'when proxy_conf is blank' do
20
+ should 'returns [nil, nil]' do
21
+ assert_equal [nil, nil], @cpr.send(:parse_proxy, "")
22
+ end
23
+ end
24
+
25
+ context 'when proxy_conf is "proxy.example.com"' do
26
+ should 'returns ["proxy.example.com", nil]' do
27
+ assert_equal ["proxy.example.com", nil], @cpr.send(:parse_proxy, "proxy.example.com")
28
+ end
29
+ end
30
+
31
+ context 'when proxy_conf is "proxy.example.com:8080"' do
32
+ should 'returns ["proxy.example.com", 8080]' do
33
+ assert_equal ["proxy.example.com", 8080], @cpr.send(:parse_proxy, "proxy.example.com:8080")
34
+ end
35
+ end
36
+
37
+ context 'when proxy_conf is "proxy.example.com:foo"' do
38
+ should 'returns ["proxy.example.com", nil]' do
39
+ assert_equal ["proxy.example.com", nil], @cpr.send(:parse_proxy, "proxy.example.com:foo")
40
+ end
41
+ end
42
+ end
43
+
44
+ context '#title_and_description' do
45
+ setup do
46
+ @cpr = CreatePullRequest.new nil
47
+ def @cpr.title_from_branch; 'title_from_branch'; end
48
+ def @cpr.git_commit_messages; 'git_commit_messages'; end
49
+ @options = Struct.new(:title, :description)
50
+ end
51
+
52
+ context 'with no options' do
53
+ should 'sets default title and description' do
54
+ title, descr = @cpr.send(:title_and_description, @options.new(nil, nil))
55
+ assert_equal title, 'title_from_branch'
56
+ assert_equal descr, 'git_commit_messages'
57
+ end
58
+ end
59
+
60
+ context 'with title option' do
61
+ should 'sets custom title and default description' do
62
+ title, descr = @cpr.send(:title_and_description, @options.new('custom title', nil))
63
+ assert_equal title, 'custom title'
64
+ assert_equal descr, 'git_commit_messages'
65
+ end
66
+ end
67
+
68
+ context 'with description option' do
69
+ should 'sets default title and custom description' do
70
+ title, descr = @cpr.send(:title_and_description, @options.new(nil, 'custom description'))
71
+ assert_equal title, 'title_from_branch'
72
+ assert_equal descr, 'custom description'
73
+ end
74
+ end
75
+
76
+ context 'with both title and description options' do
77
+ should 'sets custom title and description' do
78
+ title, descr = @cpr.send(:title_and_description, @options.new('custom title', 'custom description'))
79
+ assert_equal title, 'custom title'
80
+ assert_equal descr, 'custom description'
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,96 @@
1
+ require 'helper'
2
+
3
+ include Atlassian::Stash
4
+ include Atlassian::Stash::Git
5
+
6
+ class TestGit < Minitest::Test
7
+
8
+ should "extract remote with ssh remote" do
9
+ Atlassian::Stash::Git.stubs(:get_remotes).returns(
10
+ "origin ssh://git@stash.atlassian.com:7999/STASH/stash.git (fetch)
11
+ origin ssh://git@stash.atlassian.com:7999/STASH/stash.git (push)"
12
+ )
13
+ assert_equal 'ssh://git@stash.atlassian.com:7999/STASH/stash.git', Atlassian::Stash::Git.get_remote_url
14
+ end
15
+
16
+ should "extract push remote with different fetch and push urls" do
17
+ Atlassian::Stash::Git.stubs(:get_remotes).returns(
18
+ "origin ssh://git@github.com/~sebr/stash.git (fetch)
19
+ origin ssh://git@stash.atlassian.com:7999/STASH/stash.git (push)"
20
+ )
21
+ assert_equal 'ssh://git@stash.atlassian.com:7999/STASH/stash.git', Atlassian::Stash::Git.get_remote_url
22
+ end
23
+
24
+ should "extract remote with http remote" do
25
+ Atlassian::Stash::Git.stubs(:get_remotes).returns(
26
+ "origin http://adam@sonoma:7990/stash/scm/QA/stash.git (fetch)
27
+ origin http://adam@sonoma:7990/stash/scm/QA/stash.git (push)"
28
+ )
29
+ assert_equal 'http://adam@sonoma:7990/stash/scm/QA/stash.git', Atlassian::Stash::Git.get_remote_url
30
+ end
31
+
32
+ should "extract remote with multiple remote urls" do
33
+ Atlassian::Stash::Git.stubs(:get_remotes).returns(
34
+ "bitbucket git@bitbucket.org:atlassian/stash-command-line-tools.git (fetch)
35
+ bitbucket git@bitbucket.org:atlassian/stash-command-line-tools.git (push)
36
+ kostya http://admin@kostya:7990/scm/CA/cylon.git (fetch)
37
+ kostya http://admin@kostya:7990/scm/CA/cylon.git (push)
38
+ local http://delirium:7990/git/STASH/stash.git (fetch)
39
+ local http://delirium:7990/git/STASH/stash.git (push)
40
+ origin ssh://git@stash.atlassian.com:7999/STASH/stash.git (fetch)
41
+ origin ssh://git@stash.atlassian.com:7999/STASH/stash.git (push)
42
+ seb http://adam@sonoma:7990/stash/scm/QA/stash.git (fetch)
43
+ seb http://adam@sonoma:7990/stash/scm/QA/stash.git (push)
44
+ upstream http://github-enterprise-11-10/stash/stash.git (fetch)
45
+ upstream http://github-enterprise-11-10/stash/stash.git (push)")
46
+ assert_equal 'ssh://git@stash.atlassian.com:7999/STASH/stash.git', Atlassian::Stash::Git.get_remote_url
47
+ end
48
+
49
+ should "extract custom remote with multiple remote urls" do
50
+ Atlassian::Stash::Git.stubs(:get_remotes).returns(
51
+ "bitbucket git@bitbucket.org:atlassian/stash-command-line-tools.git (fetch)
52
+ bitbucket git@bitbucket.org:atlassian/stash-command-line-tools.git (push)
53
+ kostya http://admin@kostya:7990/scm/CA/cylon.git (fetch)
54
+ kostya http://admin@kostya:7990/scm/CA/cylon.git (push)
55
+ local http://delirium:7990/git/STASH/stash.git (fetch)
56
+ local http://delirium:7990/git/STASH/stash.git (push)
57
+ origin ssh://git@stash.atlassian.com:7999/STASH/stash.git (fetch)
58
+ origin ssh://git@stash.atlassian.com:7999/STASH/stash.git (push)
59
+ upstream ssh://git@stash.atlassian.com:7999/ATLASSIAN/stash.git (fetch)
60
+ upstream ssh://git@stash.atlassian.com:7999/ATLASSIAN/stash.git (push)
61
+ seb http://adam@sonoma:7990/stash/scm/QA/stash.git (fetch)
62
+ seb http://adam@sonoma:7990/stash/scm/QA/stash.git (push)
63
+ upstream http://github-enterprise-11-10/stash/stash.git (fetch)
64
+ upstream http://github-enterprise-11-10/stash/stash.git (push)")
65
+ assert_equal 'ssh://git@stash.atlassian.com:7999/ATLASSIAN/stash.git', Atlassian::Stash::Git.get_remote_url('upstream')
66
+ end
67
+
68
+ should "repo with no remotes returns nil" do
69
+ Atlassian::Stash::Git.stubs(:get_remotes).returns("")
70
+ assert_equal nil, Atlassian::Stash::Git.get_remote_url
71
+ end
72
+
73
+ should "repo with unfound remote returns nil" do
74
+ Atlassian::Stash::Git.stubs(:get_remotes).returns(
75
+ "bitbucket git@bitbucket.org:atlassian/stash-command-line-tools.git (fetch)
76
+ bitbucket git@bitbucket.org:atlassian/stash-command-line-tools.git (push)"
77
+ )
78
+ assert_equal nil, Atlassian::Stash::Git.get_remote_url('origin')
79
+ end
80
+
81
+ should "branches are matched" do
82
+ Atlassian::Stash::Git.stubs(:get_branches).returns(
83
+ " feature
84
+ temp
85
+ * master
86
+ remotes/origin/master
87
+ remotes/origin/release/v1.0
88
+ remotes/origin/feature/awesome
89
+ remotes/upstream/master
90
+ remotes/upstream/release/v1.0
91
+ remotes/upstream/feature/Issue7")
92
+ assert_equal true, Atlassian::Stash::Git.is_branch?('master')
93
+ assert_equal true, Atlassian::Stash::Git.is_branch?('remotes/upstream/master')
94
+ end
95
+
96
+ end