cipr 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []