cipr 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Mark Simoneau
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Cipr #
2
+
3
+ Cipr is continuous integration for your pull requests. It detects new pull reqests or changed pull requests and runs specs on them
4
+
5
+ ## Basic Usage ##
6
+
7
+ gem install cipr
8
+ cipr [-u <github_user> [-p <github_password> | -t <github_token>]] http://github.com/<github_user>/<github_repo>
9
+
10
+ ## Configuration ##
11
+
12
+ By default, cipr does the following
13
+
14
+ Watches your repository for open pull requests
15
+ Upon finding one, clones the repository in a new directory, applies the pull request patch, and runs 'rake spec' on it
16
+ The results are added as a comment to the pull request
17
+
18
+ If you have private repositories, you'll need to set up your ~/.netrc file like so:
19
+
20
+ machine github.com
21
+ login <github_username>
22
+ password <github_password>
23
+
24
+ ## TODO ##
25
+
26
+ Provide a mechanism for defining what to run on a repo
27
+
28
+ ## About ##
29
+
30
+ Cipr (pronounced like sipper) is continuous integration for pull requests
data/bin/cipr ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'choice'
5
+ require 'cipr'
6
+
7
+ Choice.options do
8
+ banner "Usage: #{File.basename(__FILE__)} [-u <github_user>] [-p <github_password>] [-d] user/repo"
9
+ header ''
10
+ header 'Github Options:'
11
+
12
+ option :user do
13
+ short '-u'
14
+ long '--user=USERNAME'
15
+ desc "Github username for authentication"
16
+ end
17
+
18
+ option :password do
19
+ short '-p'
20
+ long '--password=PASSWORD'
21
+ desc "Github password for authentication"
22
+ end
23
+
24
+ option :directory do
25
+ short '-d'
26
+ long '--directory=GIT_REPO_DIRECTORY'
27
+ desc "Github repo location"
28
+ end
29
+
30
+ option :command do
31
+ short '-c'
32
+ long '--command=COMMAND_TO_RUN'
33
+ desc "Command to run to test, defaults to 'rake spec'"
34
+ end
35
+
36
+
37
+ separator ''
38
+ separator 'Common options: '
39
+
40
+ option :help do
41
+ long '--help'
42
+ desc 'Show this message'
43
+ end
44
+
45
+ option :version do
46
+ short '-v'
47
+ long '--version'
48
+ desc 'Show version'
49
+ action do
50
+ puts "#{File.basename(__FILE__)} v#{Cipr::Version}"
51
+ exit
52
+ end
53
+ end
54
+ end
55
+
56
+ options = Choice.choices
57
+
58
+ Cipr::Server.go(Choice.rest[0].to_s, options)
@@ -0,0 +1,120 @@
1
+ require 'httparty'
2
+ require 'git'
3
+
4
+ module Cipr
5
+ class Repo
6
+ include HTTParty
7
+ base_uri 'https://api.github.com'
8
+
9
+ def initialize(repo, options={})
10
+ @repo_user, @repo = repo.split("/")
11
+ @directory = options[:directory]
12
+ @prep = options[:prep] || 'bundle && rake db:migrate'
13
+ @command = options[:command] || 'bundle exec rake spec'
14
+ @auth = {:username => options[:user] || @repo_user, :password => options[:password]}.reject {|k,v| v.nil?}
15
+ end
16
+
17
+ def pull_requests(state=:open)
18
+ get("/repos/#@repo_user/#@repo/pulls", :query => {:state => state}).map {|p| PullRequest.new(self, p)}
19
+ end
20
+
21
+ def pull_request(id)
22
+ PullRequest.new(self, get("/repos/#@repo_user/#@repo/pulls/#{id}"))
23
+ end
24
+
25
+ def comment(issue_id, body)
26
+ post("/repos/#@repo_user/#@repo/issues/#{issue_id}/comments", :body => {'body' => body}.to_json)
27
+ end
28
+
29
+ def test
30
+ tested = 0
31
+ pull_requests.each do |pr|
32
+ pr = pull_request(pr.number)
33
+ next if pr.merged?
34
+
35
+ ready = false
36
+ if pr.mergeable?
37
+ puts "Applying Pull Request ##{pr.number}"
38
+ ready = pr.apply =~ /Updating/
39
+ else
40
+ puts "Checking out Pull Request ##{pr.number}"
41
+ pr.checkout
42
+ ready = true
43
+ end
44
+
45
+ if ready
46
+ puts "Testing Pull Request ##{pr.number}"
47
+ output = execute
48
+ unless output.nil? || output.strip.empty?
49
+ pr.comment(output)
50
+ tested += 1
51
+ end
52
+ end
53
+ end
54
+ puts "Tested #{tested} pull requests this round..."
55
+ tested
56
+ end
57
+
58
+ def git
59
+ @g ||= @directory ? Git.open(@directory) : Git.clone("https://github.com/#@repo_user/#@repo.git", @repo)
60
+ end
61
+
62
+ def apply_pull_request(pr)
63
+ pr = pull_request(pr.respond_to?(:number) ? pr.number : pr)
64
+ git.branch(pr.merge_to).checkout
65
+ git.branch("#{pr.username}-#{pr.merge_from}").checkout
66
+ git.add_remote(pr.username, pr.pull_repo) unless git.remotes.map(&:to_s).include?(pr.username)
67
+ git.pull(pr.username, "#{pr.username}/#{pr.merge_from}", "Merge pull request ##{pr.number} - #{pr.title}")
68
+ end
69
+
70
+ def checkout_pull_request(pr)
71
+ pr = pull_request(pr.respond_to?(:number) ? pr.number : pr)
72
+ git.branch(pr.merge_to).checkout
73
+ git.add_remote(pr.username, pr.pull_repo) unless git.remotes.map(&:to_s).include?(pr.username)
74
+ git.branch("#{pr.username}/#{pr.merge_from}").checkout
75
+ end
76
+
77
+ def auth_string
78
+ [@auth[:username], @auth[:password]].compact.join(":")
79
+ end
80
+
81
+ private
82
+
83
+ def execute
84
+ puts @prep
85
+ `/bin/bash -l -c 'cd #{git.dir} && #{@prep}'`
86
+ puts @command
87
+ `/bin/bash -l -c 'cd #{git.dir} && #{@command} > /tmp/test_output.txt'`
88
+ result = File.open("/tmp/test_output.txt").gets
89
+ split_comment(result, 80)
90
+ end
91
+
92
+ def split_comment(comment, line_length=80)
93
+ comment.split("\n").map do |l|
94
+ p = []
95
+ while l.length > line_length
96
+ p << l[0..(line_length-1)]
97
+ l = l[line_length..-1]
98
+ end
99
+ p << l
100
+ p.join("\n")
101
+ end.join("\n")
102
+ end
103
+
104
+
105
+ def get(path, options={})
106
+ options.merge!(:basic_auth => @auth)
107
+ self.class.get(path, options)
108
+ end
109
+
110
+ def post(path, options={})
111
+ options.merge!(:basic_auth => @auth)
112
+ self.class.post(path, @auth.merge(options))
113
+ end
114
+
115
+ def put(path, options={})
116
+ options.merge!(:basic_auth => @auth)
117
+ self.class.put(path, @auth.merge(options))
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,51 @@
1
+ require 'ostruct'
2
+ module Cipr
3
+ class PullRequest < OpenStruct
4
+ def initialize(repo, *args)
5
+ @repo = repo
6
+ super(*args)
7
+ end
8
+
9
+ def comment(body)
10
+ @repo.comment(self.number, body)
11
+ end
12
+
13
+ def mergeable?
14
+ mergeable
15
+ end
16
+
17
+ def merged?
18
+ merged
19
+ end
20
+
21
+ def username
22
+ head['user']['login']
23
+ end
24
+
25
+ def merge_to
26
+ base['ref']
27
+ end
28
+
29
+ def merge_from
30
+ head['ref']
31
+ end
32
+
33
+ def pull_repo
34
+ auth = @repo.auth_string
35
+ if auth && !auth.empty?
36
+ head['repo']['clone_url'].sub("https://", "https://#{auth}@")
37
+ else
38
+ head['repo']['clone_url']
39
+ end
40
+ end
41
+
42
+ def apply
43
+ @repo.apply_pull_request(self)
44
+ end
45
+
46
+ def checkout
47
+ @repo.checkout_pull_request(self)
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,3 @@
1
+ module Cipr
2
+ Version = VERSION = "0.1.0"
3
+ end
data/lib/cipr.rb ADDED
@@ -0,0 +1,21 @@
1
+ require_relative 'cipr/version'
2
+ require_relative 'cipr/github'
3
+ require_relative 'cipr/pull_request'
4
+
5
+ module Cipr
6
+ class Server
7
+ def self.go(repo, options={})
8
+ wait_time = 1
9
+ c = Cipr::Repo.new(repo, options)
10
+ while true
11
+ puts "Checking for Pull Requests..."
12
+ if c.test > 0
13
+ wait_time = 2
14
+ else
15
+ wait_time += 2 if wait_time < 30
16
+ end
17
+ sleep 60*wait_time
18
+ end
19
+ end
20
+ end
21
+ end
data/spec/github.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'cipr'
2
+ describe Cipr::Github do
3
+
4
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cipr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Mark Simoneau
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-10 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: choice
16
+ requirement: &70312260471920 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70312260471920
25
+ - !ruby/object:Gem::Dependency
26
+ name: github_api
27
+ requirement: &70312260470680 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70312260470680
36
+ - !ruby/object:Gem::Dependency
37
+ name: git
38
+ requirement: &70312260469340 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70312260469340
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec
49
+ requirement: &70312260467920 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70312260467920
58
+ description: cipr tests your pull requests and comments the results
59
+ email: mark@quarternotecoda.com
60
+ executables:
61
+ - cipr
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - README.md
66
+ - LICENSE
67
+ - lib/cipr/github.rb
68
+ - lib/cipr/pull_request.rb
69
+ - lib/cipr/version.rb
70
+ - lib/cipr.rb
71
+ - bin/cipr
72
+ - spec/github.rb
73
+ homepage: http://github.com/marksim/cipr
74
+ licenses: []
75
+ post_install_message:
76
+ rdoc_options: []
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ segments:
86
+ - 0
87
+ hash: 2263140989473189672
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ segments:
95
+ - 0
96
+ hash: 2263140989473189672
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 1.8.10
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: cipr tests your pull requests.
103
+ test_files: []