github-control 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.dirname(__FILE__) + '/../lib/github-control'
4
+
5
+ GithubControl::CLI.execute(ARGV)
@@ -0,0 +1,5 @@
1
+ ---
2
+ user:
3
+ name: fakie
4
+ token: fakie
5
+ password: fakie
@@ -0,0 +1,35 @@
1
+ require 'yaml'
2
+
3
+ begin
4
+ require 'json'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'json'
8
+ end
9
+
10
+ require 'rest_client'
11
+ require 'nokogiri'
12
+
13
+ module GithubControl
14
+ class Error < StandardError; end
15
+ class APIError < Error; end
16
+ class ProblemWithOptions < Error; end
17
+ end
18
+
19
+ $:.unshift File.dirname(__FILE__)
20
+
21
+ require 'github-control/version'
22
+ require 'github-control/cli'
23
+ require 'github-control/console'
24
+ require 'github-control/user'
25
+ require 'github-control/repository'
26
+ require 'github-control/repositories'
27
+ require 'github-control/collaborators'
28
+ require 'github-control/post_receive_urls'
29
+
30
+ require 'github-control/action'
31
+ require 'github-control/actions/repositories'
32
+ require 'github-control/actions/shell'
33
+ require 'github-control/actions/collaborators'
34
+ require 'github-control/actions/add_collaborators'
35
+ require 'github-control/actions/remove_collaborators'
@@ -0,0 +1,19 @@
1
+ module GithubControl
2
+ class Action
3
+ def initialize(cli)
4
+ @cli = cli
5
+ end
6
+
7
+ def options
8
+ @options ||= {}
9
+ end
10
+
11
+ def add_options(parser)
12
+ raise NotImplementedError, "Please implement the #{self.class}#add_options method"
13
+ end
14
+
15
+ def call
16
+ raise NotImplementedError, "Please implement the #{self.class}#call method"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ module GithubControl
2
+ module Actions
3
+ class AddCollaborators < Action
4
+ def add_options(parser)
5
+ parser.on("-R name", "--repository name", "The repository on Github") do |repository_name|
6
+ options[:repository_name] = repository_name
7
+ end
8
+
9
+ parser.on("-U name", "--user name", "The user on Github") do |user_name|
10
+ options[:user_name] = user_name
11
+ end
12
+ end
13
+
14
+ def call
15
+ puts "Adding #{user.name} to the collaborators of #{repository.full_name}"
16
+ puts "-" * 40
17
+ if repository.collaborators.include?(user)
18
+ puts "#{user.name} is already a collaborator"
19
+ else
20
+ repository.collaborators << user
21
+ puts "Done"
22
+ end
23
+ end
24
+
25
+ def repository
26
+ @cli.console.current_user.repo_for(options[:repository_name] || raise(ProblemWithOptions, "Please specify a repository"))
27
+ end
28
+
29
+ def user
30
+ @cli.console.user_for(options[:user_name] || raise(ProblemWithOptions, "Please specify a user"))
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ module GithubControl
2
+ module Actions
3
+ class Collaborators < Action
4
+ def add_options(parser)
5
+ parser.on("-R name", "--repository name", "The repository on Github") do |repository_name|
6
+ options[:repository_name] = repository_name
7
+ end
8
+
9
+ parser.on("-U name", "--user name", "The user on Github") do |user_name|
10
+ options[:user_name] = user_name
11
+ end
12
+ end
13
+
14
+ def call
15
+ puts "Listing the collaborators of #{repository.full_name}"
16
+ puts "-" * 40
17
+ repository.collaborators.each do |user|
18
+ puts "- #{user.name}"
19
+ end
20
+ end
21
+
22
+ def repository
23
+ user.repo_for(options[:repository_name] || raise(ProblemWithOptions, "Please specify a repository"))
24
+ end
25
+
26
+ def user
27
+ @cli.console.user_for(options[:user_name] || raise(ProblemWithOptions, "Please specify a user"))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ module GithubControl
2
+ module Actions
3
+ class RemoveCollaborators < Action
4
+ def add_options(parser)
5
+ parser.on("-R name", "--repository name", "The repository on Github") do |repository_name|
6
+ options[:repository_name] = repository_name
7
+ end
8
+
9
+ parser.on("-U name", "--user name", "The user on Github") do |user_name|
10
+ options[:user_name] = user_name
11
+ end
12
+ end
13
+
14
+ def call
15
+ puts "Removing #{user.name} from the collaborators of #{repository.full_name}"
16
+ puts "-" * 40
17
+ unless repository.collaborators.include?(user)
18
+ puts "#{user.name} is not a collaborator"
19
+ else
20
+ repository.collaborators.delete(user)
21
+ puts "Done"
22
+ end
23
+ end
24
+
25
+ def repository
26
+ @cli.console.current_user.repo_for(options[:repository_name] || raise(ProblemWithOptions, "Please specify a repository"))
27
+ end
28
+
29
+ def user
30
+ @cli.console.user_for(options[:user_name] || raise(ProblemWithOptions, "Please specify a user"))
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ module GithubControl
2
+ module Actions
3
+ class Repositories < Action
4
+ def add_options(parser)
5
+ parser.on("-U name", "--user name", "The user to list the repositories") do |user_name|
6
+ options[:user_name] = user_name
7
+ end
8
+ end
9
+
10
+ def call
11
+ puts "Repositories for #{user.name}"
12
+ puts "-" * 40
13
+ user.repositories.each do |repo|
14
+ puts "- #{repo.name}"
15
+ end
16
+ end
17
+
18
+ def user
19
+ @cli.console.user_for(options[:user_name] || @cli.console.current_user.name)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'irb'
2
+
3
+ module GithubControl
4
+ module Actions
5
+ class Shell < Action
6
+ def add_options(parser)
7
+ end
8
+
9
+ def call
10
+ console = @cli.console
11
+ puts "You are now able to interact with Github via the 'github' method"
12
+ Object.send(:define_method, :github) { console }
13
+ IRB.start
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,105 @@
1
+ require 'optparse'
2
+
3
+ module GithubControl
4
+ class CLI
5
+ def self.execute(args)
6
+ new(args).run
7
+ end
8
+
9
+ def initialize(args)
10
+ @args = args
11
+ end
12
+
13
+ def run
14
+ current_action.add_options(option_parser)
15
+ option_parser.parse!(@args)
16
+ current_action.call
17
+ rescue ProblemWithOptions, OptionParser::ParseError => e
18
+ puts e.backtrace
19
+ $stderr.puts
20
+ $stderr.puts e.message
21
+ $stderr.puts
22
+ $stderr.puts option_parser
23
+ exit 2
24
+ end
25
+
26
+ def current_action
27
+ @current_action ||= create_action
28
+ end
29
+
30
+ def create_action
31
+ case action_name
32
+ when "list"
33
+ Actions::Repositories.new(self)
34
+ when "shell"
35
+ Actions::Shell.new(self)
36
+ when "collab"
37
+ Actions::Collaborators.new(self)
38
+ when "add_collab"
39
+ Actions::AddCollaborators.new(self)
40
+ when "remove_collab"
41
+ Actions::RemoveCollaborators.new(self)
42
+ else
43
+ raise ProblemWithOptions, "#{action_name} is not a valid action"
44
+ end
45
+ end
46
+
47
+ def action_name
48
+ @action_name ||= @args.shift || raise(ProblemWithOptions, "Please provide an action to run")
49
+ end
50
+
51
+ def console
52
+ @console ||= Console.new(user_config)
53
+ end
54
+
55
+ def user_config
56
+ config["user"] || raise(ProblemWithOptions, "You need to provide user data in the YAML file")
57
+ end
58
+
59
+ def config
60
+ @config ||= YAML.load_file(config_filename)
61
+ end
62
+
63
+ def config_filename
64
+ options[:config_filename] || raise(ProblemWithOptions, "You need to provide the path to the YAML filename")
65
+ end
66
+
67
+ def options
68
+ @options ||= {
69
+ :debug => false,
70
+ :argv => @args,
71
+ }
72
+ end
73
+
74
+ def option_parser
75
+ @option_parser ||= OptionParser.new do |opts|
76
+ opts.banner = "Usage: github-control #{@action_name || '[action]'} [options] ..."
77
+
78
+ opts.on_tail("-h", "--help", "Show this message") do
79
+ $stderr.puts opts
80
+ exit
81
+ end
82
+
83
+ opts.on("--debug", "Display debugging information") {
84
+ options[:debug] = true
85
+ $debug = true
86
+ }
87
+
88
+ opts.on("-c filename", "--config filename", "Load the config from this YAML file") do |filename|
89
+ options[:config_filename] = filename
90
+ end
91
+
92
+ opts.on("-v", "--version", "Display the github version, and exit.") do
93
+ puts "Github Control version #{GithubControl::VERSION}"
94
+ exit
95
+ end
96
+
97
+ opts.separator ""
98
+ end
99
+ end
100
+
101
+ def inspect
102
+ "#<#{self.class} logged in as #{current_user.name.inspect}>"
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,36 @@
1
+ module GithubControl
2
+ class Collaborators
3
+ include Enumerable
4
+
5
+ attr_reader :repository
6
+
7
+ def initialize(repository)
8
+ @repository = repository
9
+ end
10
+
11
+ def create(user)
12
+ @repository.owner.cli.post("/repos/collaborators/" \
13
+ "#{@repository.name}/add/#{user}")
14
+ end
15
+
16
+ def delete(user)
17
+ @repository.owner.cli.post("/repos/collaborators/" \
18
+ "#{@repository.name}/remove/#{user}")
19
+ end
20
+
21
+ def each(&block)
22
+ set.each(&block)
23
+ end
24
+
25
+ def set
26
+ @set ||= json_data["collaborators"].map { |name|
27
+ @repository.owner.cli.user_for(name)
28
+ }
29
+ end
30
+
31
+ def json_data
32
+ @repository.owner.cli.post("/repos/show/" \
33
+ "#{@repository.owner.name}/#{@repository.name}/collaborators")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,63 @@
1
+ module GithubControl
2
+ class Console
3
+ def initialize(user_config)
4
+ @user_config = user_config
5
+ @users = {}
6
+ end
7
+
8
+ def current_user
9
+ @current_user ||= user_for(user_name)
10
+ end
11
+
12
+ def user_for(name)
13
+ @users[name] ||= User.new(self, name)
14
+ end
15
+
16
+ def post(path, params={})
17
+ params.merge!(:login => user_name, :token => user_token)
18
+ data = JSON.parse(RestClient.post(url_for(path), params))
19
+ if error = data["error"]
20
+ raise APIError, error.first["error"]
21
+ else
22
+ data
23
+ end
24
+ end
25
+
26
+ def get(path, params={})
27
+ RestClient.get(url_for(path), params)
28
+ end
29
+
30
+ def url_for(path)
31
+ "http://github.com/api/v2/json#{path}"
32
+ end
33
+
34
+ def cookies
35
+ @cookies ||= session_cookie
36
+ end
37
+
38
+ def session_cookie
39
+ response = raw_post("/session", {:login => user_name,
40
+ :password => user_password}, {}, :auto_redirect => false)
41
+ response.headers[:set_cookie].split(";").first
42
+ end
43
+
44
+ def user_name
45
+ @user_config["name"] ||
46
+ raise(ProblemWithOptions, "You need to provide the user name in the YAML file")
47
+ end
48
+
49
+ def user_token
50
+ @user_config["token"] ||
51
+ raise(ProblemWithOptions, "You need to provide the user token in the YAML file")
52
+ end
53
+
54
+ def user_password
55
+ @user_config["password"] ||
56
+ raise(ProblemWithOptions, "You need to provide the user password in the YAML file")
57
+ end
58
+
59
+ def inspect
60
+ "#<#{self.class} logged in as #{current_user.name.inspect}>"
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,54 @@
1
+ module GithubControl
2
+ class PostReceiveUrls
3
+ include Enumerable
4
+
5
+ def initialize(repository)
6
+ @repository = repository
7
+ end
8
+ attr_reader :repository
9
+
10
+ def <<(url)
11
+ update_with(set + [url])
12
+ end
13
+
14
+ def delete(url)
15
+ update_with(set - [url])
16
+ end
17
+
18
+ def clear
19
+ update_with([])
20
+ end
21
+
22
+ def update_with(urls)
23
+ form_data = []
24
+ urls.each do |url|
25
+ value = URI.escape(url.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
26
+ form_data << "urls[]=#{value}"
27
+ end
28
+ @repository.owner.cli.scrape_post("/#{@repository.owner.name}/#{@repository.name}/edit/postreceive_urls",
29
+ form_data.join("&"), :accept => 'text/javascript')
30
+ end
31
+
32
+ def empty?
33
+ set.empty?
34
+ end
35
+
36
+ def each(&block)
37
+ set.each(&block)
38
+ end
39
+
40
+ def set
41
+ set = []
42
+ doc = Nokogiri::HTML(html_data)
43
+ doc.search("fieldset#postreceive_urls.service-hook form p input[name='urls[]']").each do |input|
44
+ set << input["value"] if input["value"]
45
+ end
46
+ set
47
+ end
48
+
49
+ def html_data
50
+ @repository.owner.cli.scrape_get("/#{@repository.owner.name}/#{@repository.name}/edit/hooks")
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,61 @@
1
+ module GithubControl
2
+ class Repositories
3
+ include Enumerable
4
+
5
+ def initialize(user)
6
+ @user = user
7
+ end
8
+
9
+ def create(name, access)
10
+ @user.cli.post("/repos/create",
11
+ :name => name,
12
+ :public => access == :public ? 1 : 0
13
+ )
14
+
15
+ repo = Repository.new(@user, name, access)
16
+ set << repo
17
+ repo
18
+ end
19
+
20
+ def [](name)
21
+ set.detect { |r| r.name == name }
22
+ end
23
+
24
+ def delete(name)
25
+ response = @user.cli.post("/repos/delete/#{name}")
26
+ @user.cli.post("/repos/delete/#{name}",
27
+ :delete_token => response["delete_token"])
28
+ any? && set.delete(name)
29
+ end
30
+
31
+ def destroy
32
+ each { |r| delete(r.name) }
33
+ end
34
+
35
+ def each(&block)
36
+ set.each(&block)
37
+ end
38
+
39
+ def clear
40
+ @set = nil
41
+ end
42
+
43
+ def empty?
44
+ ! any?
45
+ end
46
+
47
+ def size
48
+ set.size
49
+ end
50
+
51
+ private
52
+ def set
53
+ @set ||= @user.cli.post("/repos/show/#{@user.name}")["repositories"].
54
+ sort_by { |r| r["name"] }.
55
+ map { |r|
56
+ access = r["private"] ? :private : :public
57
+ Repository.new(@user, r["name"], access)
58
+ }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,33 @@
1
+ module GithubControl
2
+ class Repository
3
+ attr_reader :owner, :name, :access
4
+
5
+ def initialize(owner, name, access)
6
+ @owner, @name, @access = owner, name, access
7
+ end
8
+
9
+ def private?
10
+ @access == :private
11
+ end
12
+
13
+ def public?
14
+ @access == :public
15
+ end
16
+
17
+ def full_name
18
+ "#{@owner.name}/#{@name}"
19
+ end
20
+
21
+ def collaborators
22
+ @collaborators ||= Collaborators.new(self)
23
+ end
24
+
25
+ def post_receive_urls
26
+ @post_receive_urls ||= PostReceiveUrls.new(self)
27
+ end
28
+
29
+ def destroy
30
+ @owner.repositories.delete(@name)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ module GithubControl
2
+ class User
3
+ attr_reader :cli, :name
4
+
5
+ def initialize(cli, name)
6
+ @cli, @name = cli, name
7
+ end
8
+
9
+ def repositories
10
+ @repos ||= Repositories.new(self)
11
+ end
12
+
13
+ def public_repositories
14
+ repositories.select { |r| r.public? }
15
+ end
16
+
17
+ def private_repositories
18
+ repositories.select { |r| r.private? }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module GithubControl
2
+ VERSION = "0.9.0"
3
+ end
@@ -0,0 +1,53 @@
1
+ require "spec_helper"
2
+
3
+ describe "Github Control" do
4
+ def config
5
+ @config ||= YAML.load_file("spec/config.yml")["user"]
6
+ end
7
+
8
+ def console
9
+ @console ||= GithubControl::Console.new(config)
10
+ end
11
+
12
+ def current_user
13
+ @user ||= console.current_user
14
+ end
15
+
16
+ before(:all) do
17
+ RestClient.log = "restclient.log"
18
+ end
19
+
20
+ before(:each) do
21
+ current_user.repositories.destroy
22
+ end
23
+
24
+ it "creates a public repository" do
25
+ current_user.should have(:no).repositories
26
+ current_user.repositories.create("public-repo", :public)
27
+ current_user.should have(1).repositories
28
+ current_user.repositories["public-repo"].should be_public
29
+ end
30
+
31
+ it "creates a private repository" do
32
+ current_user.should have(:no).private_repositories
33
+ current_user.repositories.create("private-repo", :private)
34
+ current_user.should have(1).private_repositories
35
+ current_user.repositories["private-repo"].should be_private
36
+ end
37
+
38
+ it "creates and destroys collaborators on a repository" do
39
+ repo = current_user.repositories.create("test-repo", :public)
40
+ repo.collaborators << GithubControl::User.new("atmos", console)
41
+ repo.collaborators << GithubControl::User.new("tim", console)
42
+ repo.collaborators << GithubControl::User.new("ben", console)
43
+ repo.collaborators << GithubControl::User.new("sr", console)
44
+
45
+ repo.should have(4).collaborators
46
+
47
+ # sorry dude :(
48
+ repo.collaborators.delete("atmos")
49
+ repo.should have(3).collaborators
50
+ end
51
+
52
+ it "adds and remove post-receive urls"
53
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "Post-receive URLs" do
4
+ describe "for a repository with a single URL" do
5
+ before(:each) do
6
+ @repo = @console.current_user.repo_for("with-one-post-receive-url")
7
+ end
8
+
9
+ it "can be fetched" do
10
+ @repo.post_receive_urls.to_a.should == ["http://github-control.r.spork.in/post-receive"]
11
+ end
12
+ end
13
+
14
+ describe "for a repository with no URLs" do
15
+ before(:each) do
16
+ @repo = @console.current_user.repo_for("with-no-post-receive-urls")
17
+ @repo.post_receive_urls.clear
18
+ end
19
+
20
+ it "can be fetched" do
21
+ @repo.post_receive_urls.should be_empty
22
+ end
23
+
24
+ it "adds a new URL" do
25
+ @repo.post_receive_urls << "http://example.org"
26
+ @repo.post_receive_urls.to_a.should == ["http://example.org"]
27
+ end
28
+ end
29
+
30
+ describe "for a repository with 3 URLs" do
31
+ before(:each) do
32
+ @repo = @console.current_user.repo_for("with-three-post-receive-urls")
33
+ urls = ["http://example.com/1", "http://example.com/2", "http://example.com/3"]
34
+ @repo.post_receive_urls.update_with(urls)
35
+ end
36
+
37
+ it "can be fetched" do
38
+ @repo.post_receive_urls.to_a.should == ["http://example.com/1", "http://example.com/2", "http://example.com/3"]
39
+ end
40
+
41
+ it "adds a new URL" do
42
+ @repo.post_receive_urls.delete("http://example.com/1")
43
+ @repo.post_receive_urls.to_a.should == ["http://example.com/2", "http://example.com/3"]
44
+ end
45
+ end
46
+ end
File without changes
@@ -0,0 +1,12 @@
1
+ require 'spec'
2
+ require 'randexp'
3
+ require "pp"
4
+
5
+ require File.dirname(__FILE__) + '/../lib/github-control'
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with(:rr)
9
+ config.before(:all) do
10
+ @console = GithubControl::Console.new(YAML.load_file(File.dirname(__FILE__) + '/config.yml')["user"])
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github-control
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Tim Carey-Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-28 00:00:00 -08:00
13
+ default_executable: github-control
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rest-client
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: json
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.1.3
34
+ version:
35
+ description: github-control allows you to interact with Github through a nice ruby interface
36
+ email: tim@spork.in
37
+ executables:
38
+ - github-control
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - config/example.yml
45
+ - lib/github-control/action.rb
46
+ - lib/github-control/actions/add_collaborators.rb
47
+ - lib/github-control/actions/collaborators.rb
48
+ - lib/github-control/actions/remove_collaborators.rb
49
+ - lib/github-control/actions/repositories.rb
50
+ - lib/github-control/actions/shell.rb
51
+ - lib/github-control/cli.rb
52
+ - lib/github-control/collaborators.rb
53
+ - lib/github-control/console.rb
54
+ - lib/github-control/post_receive_urls.rb
55
+ - lib/github-control/repositories.rb
56
+ - lib/github-control/repository.rb
57
+ - lib/github-control/user.rb
58
+ - lib/github-control/version.rb
59
+ - lib/github-control.rb
60
+ - spec/github_control_spec.rb
61
+ - spec/models/post_receive_urls_spec.rb
62
+ - spec/models/user_spec.rb
63
+ - spec/spec_helper.rb
64
+ has_rdoc: true
65
+ homepage: http://github.com/halorgium/github-control
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --inline-source
71
+ - --charset=UTF-8
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ version:
86
+ requirements: []
87
+
88
+ rubyforge_project:
89
+ rubygems_version: 1.3.5
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: github-control allows you to interact with Github through a nice ruby interface
93
+ test_files: []
94
+