github-control 0.9.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.
@@ -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
+