Sutto-gitauth-gh 0.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/bin/gitauth-gh +13 -0
- data/lib/git_hub_api.rb +204 -0
- data/lib/gitauth/gh_mirror.rb +93 -0
- metadata +76 -0
data/bin/gitauth-gh
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'pathname'
|
5
|
+
require Pathname.new(__FILE__).dirname.dirname.join("lib", "gitauth", "gh_mirror")
|
6
|
+
|
7
|
+
GitAuth::Application.processing(ARGV) do |a|
|
8
|
+
|
9
|
+
a.option :user, "The user to mirror, defaults to the result of `git config --global github.user`"
|
10
|
+
a.option :token, "The token for said user, defaults to the result of `git config --global github.token`"
|
11
|
+
a.controller! :mirror, "Automatically mirrors the specific users repositories", :skip_path => true
|
12
|
+
|
13
|
+
end
|
data/lib/git_hub_api.rb
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'yaml'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'hpricot'
|
5
|
+
|
6
|
+
class GitHubApi
|
7
|
+
include HTTParty
|
8
|
+
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
base_uri 'https://github.com'
|
12
|
+
|
13
|
+
attr_accessor :user, :api_key
|
14
|
+
|
15
|
+
def initialize(user, api_key)
|
16
|
+
@user = user
|
17
|
+
@api_key = api_key
|
18
|
+
end
|
19
|
+
|
20
|
+
# Repository Manipulation
|
21
|
+
|
22
|
+
def create_repository(details = {})
|
23
|
+
name = details[:name]
|
24
|
+
description = details[:description]
|
25
|
+
homepage = details[:homepage]
|
26
|
+
is_public = (!!details.fetch(:public, false) ? 1 : 0)
|
27
|
+
if [name, description, homepage].any? { |v| v.blank? }
|
28
|
+
raise ArgumentError, "You must provide atleast a name, description and a homepage"
|
29
|
+
end
|
30
|
+
results = post('repos/create', {
|
31
|
+
:name => name,
|
32
|
+
:description => description,
|
33
|
+
:homepage => homepage,
|
34
|
+
:public => is_public
|
35
|
+
})
|
36
|
+
hash_get(results, "repository")
|
37
|
+
end
|
38
|
+
|
39
|
+
def repository(name, user = @user)
|
40
|
+
results = get("repos/show/#{user}/#{name}")
|
41
|
+
repo = hash_get(results, "repository")
|
42
|
+
repo ? Repository.new(repo, self) : nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def repositories(user = @user)
|
46
|
+
res = hash_get(get("repos/show/#{user}"), "repositories")
|
47
|
+
res.respond_to?(:map) ? res.map { |h| Repository.new(h, self) } : []
|
48
|
+
end
|
49
|
+
|
50
|
+
class Repository
|
51
|
+
|
52
|
+
class SSHPublicKey < Struct.new(:name, :id); end
|
53
|
+
|
54
|
+
attr_accessor :name, :api, :attributes, :owner
|
55
|
+
|
56
|
+
def initialize(attributes, api)
|
57
|
+
@name = attributes[:name]
|
58
|
+
@owner = attributes[:owner]
|
59
|
+
@api = api
|
60
|
+
@attributes = OpenStruct.new(attributes)
|
61
|
+
end
|
62
|
+
|
63
|
+
def destroy
|
64
|
+
token = api.post("repos/delete/#{@name}")["delete_token"]
|
65
|
+
result = api.post("repos/delete/#{@name}", :delete_token => token)
|
66
|
+
result.is_a?(Hash) && result["status"] == "deleted"
|
67
|
+
end
|
68
|
+
|
69
|
+
def private?
|
70
|
+
!!@attributes.private
|
71
|
+
end
|
72
|
+
|
73
|
+
def public?
|
74
|
+
!private?
|
75
|
+
end
|
76
|
+
|
77
|
+
# User Manipulation
|
78
|
+
|
79
|
+
def collaborators
|
80
|
+
res = api.get("repos/show/#{@owner}/#{@name}/collaborators")
|
81
|
+
api.hash_get(res, "collaborators")
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_collaborator(name)
|
85
|
+
return if name.blank?
|
86
|
+
res = api.post("repos/collaborators/#{@name}/add/#{name}")
|
87
|
+
api.hash_get(res, "collaborators")
|
88
|
+
end
|
89
|
+
|
90
|
+
def remove_collaborator(name)
|
91
|
+
res = api.post("repos/collaborators/#{@name}/remove/#{name}")
|
92
|
+
api.hash_get(res, "collaborators")
|
93
|
+
end
|
94
|
+
|
95
|
+
# Hooks
|
96
|
+
|
97
|
+
def hooks
|
98
|
+
response = Hpricot(api.get("https://github.com/#{@owner}/#{@name}/edit/hooks").to_s)
|
99
|
+
response.search("//input[@name=urls[]]").map { |e| e[:value] }.compact
|
100
|
+
end
|
101
|
+
|
102
|
+
def hooks=(list)
|
103
|
+
list = [*list].compact.uniq
|
104
|
+
query_string = list.map { |u| "urls[]=#{URI.escape(u)}" }.join("&")
|
105
|
+
post_url = "https://github.com/#{@owner}/#{@name}/edit/postreceive_urls"
|
106
|
+
api.post(post_url, query_string)
|
107
|
+
end
|
108
|
+
|
109
|
+
def add_hook(url)
|
110
|
+
self.hooks = (hooks + [url])
|
111
|
+
hooks.include?(url)
|
112
|
+
end
|
113
|
+
|
114
|
+
def remove_hook(url)
|
115
|
+
self.hooks = (hooks - [url])
|
116
|
+
!hooks.include?(url)
|
117
|
+
end
|
118
|
+
|
119
|
+
# Keys
|
120
|
+
|
121
|
+
def keys
|
122
|
+
keys = api.hash_get(api.get("repos/keys/#{@name}"), "public_keys")
|
123
|
+
return false if keys.nil?
|
124
|
+
keys.map { |k| SSHPublicKey.new(k["title"], k["id"]) }
|
125
|
+
end
|
126
|
+
|
127
|
+
def add_key(title, key)
|
128
|
+
result = api.get("repos/key/#{@name}/add", {
|
129
|
+
:title => title,
|
130
|
+
:key => key
|
131
|
+
})
|
132
|
+
keys = api.hash_get(result, "public_keys")
|
133
|
+
return false if keys.nil?
|
134
|
+
keys.map { |k| SSHPublicKey.new(k["title"], k["id"]) }
|
135
|
+
end
|
136
|
+
|
137
|
+
def remove_key(key_id)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Misc. Information
|
141
|
+
|
142
|
+
def tags
|
143
|
+
api.hash_get(api.get("repos/show/#{@owner}/#{@name}/tags"), "tags")
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
# Methods
|
149
|
+
|
150
|
+
def get(path, opts = {})
|
151
|
+
self.class.get(full_path_for(path), :query => with_auth(opts))
|
152
|
+
end
|
153
|
+
|
154
|
+
def post(path, opts = {})
|
155
|
+
self.class.post(full_path_for(path), :body => with_auth(opts))
|
156
|
+
end
|
157
|
+
|
158
|
+
def put(path, opts = {})
|
159
|
+
self.class.put(full_path_for(path), :body => with_auth(opts))
|
160
|
+
end
|
161
|
+
|
162
|
+
def delete(path, opts = {})
|
163
|
+
self.class.delete(full_path_for(path), :body => with_auth(opts))
|
164
|
+
end
|
165
|
+
|
166
|
+
def full_path_for(path, version = 2, format = 'yaml')
|
167
|
+
return path if path =~ /^https?\:\/\//i
|
168
|
+
File.join("/api/v#{version}/#{format}", path)
|
169
|
+
end
|
170
|
+
|
171
|
+
def with_auth(opts)
|
172
|
+
auth = {
|
173
|
+
:login => @user,
|
174
|
+
:token => @api_key
|
175
|
+
}
|
176
|
+
if opts.is_a?(Hash)
|
177
|
+
opts.merge(auth)
|
178
|
+
else
|
179
|
+
params = opts.to_s.strip
|
180
|
+
params << "&" if params != ""
|
181
|
+
params << auth.to_params
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def check_results!(res)
|
186
|
+
if res.is_a?(Hash) && res["error"].present?
|
187
|
+
error_msg = res["error"].to_a.map { |h| h["error"] }.join(", ")
|
188
|
+
raise Error, error_msg
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def hash_get(h, k)
|
193
|
+
check_results!(h)
|
194
|
+
h.is_a?(Hash) && h[k]
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.default
|
198
|
+
return @@default if defined?(@@default) && @@default.present?
|
199
|
+
user = `git config --global github.user`.strip
|
200
|
+
token = `git config --global github.token`.strip
|
201
|
+
@@default = self.new(user, token)
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'gitauth'
|
2
|
+
|
3
|
+
dir = Pathname.new(__FILE__).dirname.dirname
|
4
|
+
$:.unshift(dir) unless $:.include?(dir)
|
5
|
+
|
6
|
+
require 'git_hub_api'
|
7
|
+
|
8
|
+
module GitAuth
|
9
|
+
class GitHubMirror
|
10
|
+
include GitAuth::Loggable
|
11
|
+
|
12
|
+
Project = Struct.new(:name, :github_clone_url, :repository)
|
13
|
+
|
14
|
+
VERSION = [0, 0, 1, 0]
|
15
|
+
|
16
|
+
def initialize(username, token)
|
17
|
+
@api = GitHubApi.new(username, token)
|
18
|
+
end
|
19
|
+
|
20
|
+
def projects
|
21
|
+
@projects ||= @api.repositories.map do |repository|
|
22
|
+
local_repo = GitAuth::Repo.get(repository.name)
|
23
|
+
Project.new(repository.name, github_url_for_repo(repository), local_repo)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def mirror!(p)
|
28
|
+
mirrored?(p) ? update!(p) : clone!(p)
|
29
|
+
end
|
30
|
+
|
31
|
+
def update!(p)
|
32
|
+
return unless p.repository
|
33
|
+
Dir.chdir(p.repository.real_path) do
|
34
|
+
GitAuth.run "git pull origin master --force"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def clone!(p)
|
39
|
+
if repository = GitAuth::Repo.create(p.name, "#{p.name}.git")
|
40
|
+
FileUtils.rm_rf(repository.real_path)
|
41
|
+
path = repository.real_path
|
42
|
+
Dir.chdir(File.dirname(path)) do
|
43
|
+
GitAuth.run "git clone --mirror #{p.github_clone_url} #{File.basename(path)}"
|
44
|
+
end
|
45
|
+
p.repository = repository
|
46
|
+
else
|
47
|
+
raise "Error creating local mirror of repository '#{p.name}'"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def mirror_all
|
52
|
+
logger.info "Mirroring all projects"
|
53
|
+
projects.each do |project|
|
54
|
+
logger.info "Mirroring for #{project.name} (#{project.github_clone_url})"
|
55
|
+
mirror!(project)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class << self
|
60
|
+
|
61
|
+
def run(options = {})
|
62
|
+
options = GitAuth::Nash.new(options)
|
63
|
+
options.user = `git config --global github.user`.strip unless options.user?
|
64
|
+
options.token = `git config --global github.token`.strip unless options.token?
|
65
|
+
logger.info "Preparing to run GitHub mirror for #{options.user}"
|
66
|
+
mirror = self.new(options.user, options.token)
|
67
|
+
mirror.mirror_all
|
68
|
+
rescue Exception => e
|
69
|
+
logger.fatal "Got Exception: #{e.class.name} - #{e.message}"
|
70
|
+
e.backtrace.each { |l| logger.fatal "--> #{l}" }
|
71
|
+
end
|
72
|
+
|
73
|
+
def version(include_path = false)
|
74
|
+
VERSION[0, (include_path ? 4 : 3)].join(".")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def github_url_for_repo(repo)
|
82
|
+
p = repo.private?
|
83
|
+
"git#{p ? "@" : "://"}github.com#{p ? ":" : "/"}#{repo.owner}/#{repo.name}.git"
|
84
|
+
end
|
85
|
+
|
86
|
+
def mirrored?(repo)
|
87
|
+
repo.repository.present?
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
GitAuth::Loader.register_controller :mirror, GitAuth::GitHubMirror
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: Sutto-gitauth-gh
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Darcy Laycock
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-17 00:00:00 -07:00
|
13
|
+
default_executable: gitauth-gh
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: brownbeagle-gitauth
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.4.5
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: httparty
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: sutto@sutto.net
|
37
|
+
executables:
|
38
|
+
- gitauth-gh
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- bin/gitauth-gh
|
45
|
+
- lib/git_hub_api.rb
|
46
|
+
- lib/gitauth
|
47
|
+
- lib/gitauth/gh_mirror.rb
|
48
|
+
has_rdoc: false
|
49
|
+
homepage: http://sutto.net/
|
50
|
+
licenses:
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.3.5
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Automatic mirror for github -> gitauth
|
75
|
+
test_files: []
|
76
|
+
|