github-backup 0.10.0 → 0.11.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.
- checksums.yaml +5 -5
- data/.travis.yml +5 -0
- data/Gemfile.lock +13 -4
- data/README.md +4 -1
- data/Rakefile +8 -0
- data/bin/github-backup +15 -3
- data/github-backup.gemspec +4 -0
- data/lib/github-backup/backup.rb +26 -71
- data/lib/github-backup/config.rb +10 -4
- data/lib/github-backup/gist.rb +16 -0
- data/lib/github-backup/github_repository_collection.rb +88 -0
- data/lib/github-backup/repository.rb +42 -0
- data/lib/github-backup/shell.rb +18 -0
- data/lib/github-backup/version.rb +1 -1
- data/lib/github-backup/wiki.rb +12 -0
- data/spec/fixtures/gitconfig +2 -0
- data/spec/lib/github-backup/backup_spec.rb +84 -0
- data/spec/lib/github-backup/config_spec.rb +115 -0
- data/spec/lib/github-backup/gist_spec.rb +41 -0
- data/spec/lib/github-backup/repository_spec.rb +85 -0
- data/spec/lib/github-backup/wiki_spec.rb +28 -0
- data/spec/spec_helper.rb +267 -0
- data/spec/support/deep_struct.rb +22 -0
- metadata +60 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7cbe362a6dd02d1e857cb3fabec20bbe633a71540a1c155fb98d1d73fc7d956a
|
4
|
+
data.tar.gz: 159a68f3e3b2b66f720475cbb86de2c582d02e837751c90a2708f79047907769
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56ba187b5dbae8b4a2321d1ed70d7ce8a3cd680257587da202f4a67297222534c1549dfabe0c494109fc6d1c9404ace3d918a43e9c56f6eb5a0c83033178016d
|
7
|
+
data.tar.gz: b7a171b16b6bf0d825df73be89baba07c7d26c27165264cdb7e345f248b480afc4f33a0b8e1ea9bce84948e2f31b3c62dcf06230564b6336e0f0127ebf2afddf
|
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -1,18 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
github-backup (0.
|
4
|
+
github-backup (0.11.0)
|
5
5
|
octokit (~> 3.7)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: http://rubygems.org/
|
9
9
|
specs:
|
10
|
-
addressable (2.3.
|
11
|
-
|
10
|
+
addressable (2.3.8)
|
11
|
+
fakefs (0.6.7)
|
12
|
+
faraday (0.9.2)
|
12
13
|
multipart-post (>= 1.2, < 3)
|
14
|
+
minitest (5.5.1)
|
13
15
|
multipart-post (2.0.0)
|
14
|
-
octokit (3.
|
16
|
+
octokit (3.8.0)
|
15
17
|
sawyer (~> 0.6.0, >= 0.5.3)
|
18
|
+
rake (12.3.1)
|
16
19
|
sawyer (0.6.0)
|
17
20
|
addressable (~> 2.3.5)
|
18
21
|
faraday (~> 0.8, < 0.10)
|
@@ -21,4 +24,10 @@ PLATFORMS
|
|
21
24
|
ruby
|
22
25
|
|
23
26
|
DEPENDENCIES
|
27
|
+
fakefs (~> 0.6.7)
|
24
28
|
github-backup!
|
29
|
+
minitest (~> 5.5.0)
|
30
|
+
rake
|
31
|
+
|
32
|
+
BUNDLED WITH
|
33
|
+
1.16.2
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
# github-backup
|
2
|
+
|
3
|
+
[](https://travis-ci.org/ddollar/github-backup)
|
4
|
+
[](https://codeclimate.com/github/ddollar/github-backup)
|
2
5
|
|
3
6
|
Back up your Github repositories locally.
|
4
7
|
|
data/Rakefile
ADDED
data/bin/github-backup
CHANGED
@@ -7,15 +7,27 @@ require 'optparse'
|
|
7
7
|
|
8
8
|
options = {}
|
9
9
|
optionparser = OptionParser.new do |opts|
|
10
|
-
opts.banner = "Usage: github-backup [options] username local_backup_root"
|
11
|
-
|
10
|
+
opts.banner = "Usage: github-backup [options] username [local_backup_root]"
|
11
|
+
|
12
12
|
opts.on("-t", "--token TOKEN", "Github API Token") do |t|
|
13
13
|
options[:token] = t
|
14
14
|
end
|
15
|
+
|
16
|
+
opts.on("--gists", "Backup username's gists") do |g|
|
17
|
+
options[:gists] = true
|
18
|
+
end
|
19
|
+
|
20
|
+
opts.on("--starred-gists", "Backup authenticated username's starred gists") do |g|
|
21
|
+
options[:starred_gists] = true
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on("--wikis", "Backup username's wikis") do |g|
|
25
|
+
options[:wikis] = true
|
26
|
+
end
|
15
27
|
end
|
16
28
|
optionparser.parse!
|
17
29
|
|
18
|
-
unless ARGV.length
|
30
|
+
unless ARGV.length >= 1
|
19
31
|
puts optionparser.help
|
20
32
|
exit 1
|
21
33
|
end
|
data/github-backup.gemspec
CHANGED
@@ -18,4 +18,8 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = "github-backup"
|
19
19
|
|
20
20
|
s.add_dependency "octokit", "~> 3.7"
|
21
|
+
|
22
|
+
s.add_development_dependency "fakefs", "~> 0.6.7"
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "minitest", "~> 5.5.0"
|
21
25
|
end
|
data/lib/github-backup/backup.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
module GithubBackup
|
2
2
|
class Backup
|
3
|
-
attr_reader :debug, :username, :client, :config
|
3
|
+
attr_reader :debug, :username, :client, :gists, :starred_gists, :wikis, :config
|
4
4
|
|
5
5
|
def initialize(username, options = {})
|
6
|
-
@username
|
7
|
-
@debug
|
8
|
-
@
|
9
|
-
@
|
6
|
+
@username = username
|
7
|
+
@debug = false
|
8
|
+
@gists = options.delete(:gists)
|
9
|
+
@starred_gists = options.delete(:starred_gists)
|
10
|
+
@wikis = options.delete(:wikis)
|
11
|
+
@config = Config.new(options)
|
12
|
+
@client = Octokit::Client.new(:access_token => config.token)
|
10
13
|
end
|
11
14
|
|
12
15
|
def execute
|
@@ -20,85 +23,37 @@ module GithubBackup
|
|
20
23
|
|
21
24
|
private
|
22
25
|
|
23
|
-
def backup_root
|
24
|
-
config.backup_root
|
25
|
-
end
|
26
|
-
|
27
|
-
def backup_directory_for(repository)
|
28
|
-
File.join(backup_root, repository.full_name) + '.git'
|
29
|
-
end
|
30
|
-
|
31
26
|
def backup_all
|
32
|
-
|
33
|
-
|
34
|
-
puts "Backing up: #{repository.full_name}"
|
35
|
-
backup_repository repository
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def repositories
|
40
|
-
repos = []
|
41
|
-
repos.concat(get_repositories_first_page)
|
27
|
+
make_backup_root
|
28
|
+
repo_collection.repos(username).each { |r| r.backup(config.backup_root) }
|
42
29
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
loop do
|
47
|
-
last_response = last_response.rels[:next].get
|
48
|
-
repos.concat(last_response.data)
|
49
|
-
break if last_response.rels[:next].nil?
|
50
|
-
end
|
30
|
+
if gists
|
31
|
+
repo_collection.gists(username).
|
32
|
+
each { |r| r.backup(config.backup_root) }
|
51
33
|
end
|
52
34
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
def get_repositories_first_page
|
57
|
-
if username_is_authenticated_user?
|
58
|
-
client.repos
|
59
|
-
elsif username_is_organisation?
|
60
|
-
client.org_repos(username)
|
61
|
-
else
|
62
|
-
client.repos(username)
|
35
|
+
if starred_gists
|
36
|
+
repo_collection.starred_gists(username).
|
37
|
+
each { |r| r.backup(config.backup_root) }
|
63
38
|
end
|
64
|
-
end
|
65
39
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
else
|
70
|
-
backup_repository_initial(repository)
|
40
|
+
if wikis
|
41
|
+
repo_collection.wikis(username).
|
42
|
+
each { |r| r.backup(config.backup_root) }
|
71
43
|
end
|
72
44
|
end
|
73
45
|
|
74
|
-
def
|
75
|
-
|
76
|
-
shell("git clone --mirror -n #{repository.ssh_url} #{repository.full_name}.git")
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def backup_repository_incremental(repository)
|
81
|
-
FileUtils::cd(backup_directory_for(repository)) do
|
82
|
-
shell("git remote update")
|
83
|
-
end
|
46
|
+
def repo_collection
|
47
|
+
@repo_collection ||= GithubBackup::GithubRepositoryCollection.new(client)
|
84
48
|
end
|
85
49
|
|
86
|
-
def
|
87
|
-
|
88
|
-
end
|
89
|
-
|
90
|
-
def username_is_authenticated_user?
|
91
|
-
return false unless client.token_authenticated?
|
92
|
-
username == client.user.login
|
50
|
+
def backup_directory_for(repository)
|
51
|
+
File.join(config.backup_root, repository.backup_path)
|
93
52
|
end
|
94
53
|
|
95
|
-
def
|
96
|
-
|
97
|
-
|
98
|
-
output = io.read
|
99
|
-
puts "OUTPUT:" if debug
|
100
|
-
puts output if debug
|
101
|
-
end
|
54
|
+
def make_backup_root
|
55
|
+
# TODO: Handle errors
|
56
|
+
FileUtils.mkdir_p(config.backup_root)
|
102
57
|
end
|
103
58
|
end
|
104
59
|
end
|
data/lib/github-backup/config.rb
CHANGED
@@ -8,9 +8,15 @@ module GithubBackup
|
|
8
8
|
attr_reader :backup_root, :gitconfig_path, :token
|
9
9
|
|
10
10
|
def initialize(options = {})
|
11
|
-
@backup_root = options.fetch(:backup_root)
|
12
|
-
@gitconfig_path = options.fetch(:gitconfig_path)
|
13
|
-
@token = options.fetch(:token)
|
11
|
+
@backup_root = options.fetch(:backup_root, nil) || Dir.pwd
|
12
|
+
@gitconfig_path = options.fetch(:gitconfig_path, nil) || DEFAULTS[:gitconfig_path]
|
13
|
+
@token = options.fetch(:token) { default_token }
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
backup_root == other.backup_root &&
|
18
|
+
gitconfig_path == other.gitconfig_path &&
|
19
|
+
token == other.token
|
14
20
|
end
|
15
21
|
|
16
22
|
private
|
@@ -26,7 +32,7 @@ module GithubBackup
|
|
26
32
|
config = {}
|
27
33
|
group = nil
|
28
34
|
|
29
|
-
return config unless File.
|
35
|
+
return config unless File.exist?(gitconfig_path)
|
30
36
|
|
31
37
|
File.foreach(gitconfig_path) do |line|
|
32
38
|
line.strip!
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative './repository'
|
2
|
+
module GithubBackup
|
3
|
+
class Gist < Repository
|
4
|
+
def clone_url
|
5
|
+
sawyer_resource.git_pull_url
|
6
|
+
end
|
7
|
+
|
8
|
+
def backup_path
|
9
|
+
if sawyer_resource.owner
|
10
|
+
"#{ sawyer_resource.owner.login }/#{ sawyer_resource.id }.git"
|
11
|
+
else
|
12
|
+
"anonymous/#{ sawyer_resource.id }.git"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module GithubBackup
|
2
|
+
class GithubRepositoryCollection
|
3
|
+
attr_reader :client
|
4
|
+
|
5
|
+
def initialize(client)
|
6
|
+
@client = client
|
7
|
+
end
|
8
|
+
|
9
|
+
def repos(username)
|
10
|
+
first_page =
|
11
|
+
if username_is_authenticated_user?(username)
|
12
|
+
client.repos
|
13
|
+
elsif username_is_organisation?(username)
|
14
|
+
client.org_repos(username)
|
15
|
+
else
|
16
|
+
client.repos(username)
|
17
|
+
end
|
18
|
+
|
19
|
+
all(first_page).map { |r| GithubBackup::Repository.new(r) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def gists(username)
|
23
|
+
first_page =
|
24
|
+
if username_is_authenticated_user?(username)
|
25
|
+
client.gists
|
26
|
+
else
|
27
|
+
client.gists(username)
|
28
|
+
end
|
29
|
+
|
30
|
+
all(first_page).map { |r| GithubBackup::Gist.new(r) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def starred_gists(username)
|
34
|
+
first_page =
|
35
|
+
if username_is_authenticated_user?(username)
|
36
|
+
client.starred_gists
|
37
|
+
else
|
38
|
+
[] # Can only list authenticated user's gists at the moment
|
39
|
+
end
|
40
|
+
|
41
|
+
return first_page if first_page.empty?
|
42
|
+
all(first_page).map { |r| GithubBackup::Gist.new(r) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def wikis(username)
|
46
|
+
first_page =
|
47
|
+
if username_is_authenticated_user?(username)
|
48
|
+
client.repos
|
49
|
+
elsif username_is_organisation?(username)
|
50
|
+
client.org_repos(username)
|
51
|
+
else
|
52
|
+
client.repos(username)
|
53
|
+
end
|
54
|
+
|
55
|
+
all(first_page).
|
56
|
+
select(&:has_wiki?).
|
57
|
+
map { |r| GithubBackup::Wiki.new(r) }
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def all(first_page)
|
63
|
+
repos = []
|
64
|
+
repos.concat(first_page)
|
65
|
+
|
66
|
+
# Iterate over paginated response
|
67
|
+
last_response = client.last_response
|
68
|
+
unless last_response.rels[:next].nil?
|
69
|
+
loop do
|
70
|
+
last_response = last_response.rels[:next].get
|
71
|
+
repos.concat(last_response.data)
|
72
|
+
break if last_response.rels[:next].nil?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
repos
|
77
|
+
end
|
78
|
+
|
79
|
+
def username_is_organisation?(username)
|
80
|
+
client.user(username).type == 'Organization'
|
81
|
+
end
|
82
|
+
|
83
|
+
def username_is_authenticated_user?(username)
|
84
|
+
return false unless client.token_authenticated?
|
85
|
+
username == client.user.login
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module GithubBackup
|
2
|
+
class Repository
|
3
|
+
attr_reader :sawyer_resource, :shell
|
4
|
+
|
5
|
+
def initialize(sawyer_resource, opts = {})
|
6
|
+
@sawyer_resource = sawyer_resource
|
7
|
+
@shell = opts[:shell] || Shell.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def backup(backup_root)
|
11
|
+
full_backup_path = File.join(backup_root, backup_path)
|
12
|
+
|
13
|
+
puts "Backing up #{ full_backup_path }"
|
14
|
+
|
15
|
+
if File.exist?(full_backup_path)
|
16
|
+
backup_repository_incremental(full_backup_path)
|
17
|
+
else
|
18
|
+
backup_repository_initial(full_backup_path)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def clone_url
|
23
|
+
sawyer_resource.ssh_url
|
24
|
+
end
|
25
|
+
|
26
|
+
def backup_path
|
27
|
+
"#{ sawyer_resource.full_name }.git"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def backup_repository_initial(path)
|
33
|
+
shell.run("git clone --mirror -n #{ clone_url } #{ path }")
|
34
|
+
end
|
35
|
+
|
36
|
+
def backup_repository_incremental(path)
|
37
|
+
FileUtils.cd(path) do
|
38
|
+
shell.run('git remote update')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module GithubBackup
|
2
|
+
class Shell
|
3
|
+
attr_reader :debug
|
4
|
+
|
5
|
+
def initialize(opts = {})
|
6
|
+
@debug = opts[:debug] || false
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(command)
|
10
|
+
puts "EXECUTING: #{command}" if debug
|
11
|
+
IO.popen(command, 'r') do |io|
|
12
|
+
output = io.read
|
13
|
+
puts "OUTPUT:" if debug
|
14
|
+
puts output if debug
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe GithubBackup::Backup do
|
4
|
+
|
5
|
+
describe :new do
|
6
|
+
|
7
|
+
# username
|
8
|
+
it 'requires a username' do
|
9
|
+
proc { GithubBackup::Backup.new }.
|
10
|
+
must_raise ArgumentError
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'initializes an Octokit client' do
|
14
|
+
backup = GithubBackup::Backup.new('ddollar', :backup_root => '/tmp')
|
15
|
+
backup.client.must_be_instance_of ::Octokit::Client
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'configures the backup' do
|
19
|
+
opts = { :backup_root => '/tmp', :token => 'S3CR3T' }
|
20
|
+
config = GithubBackup::Config.new(opts)
|
21
|
+
backup = GithubBackup::Backup.new('ddollar', opts)
|
22
|
+
backup.config.must_equal config
|
23
|
+
end
|
24
|
+
|
25
|
+
# :token
|
26
|
+
describe 'with a token' do
|
27
|
+
|
28
|
+
it 'will configure the client with an OAuth access token' do
|
29
|
+
opts = { :backup_root => '/tmp', :token => 'S3CR3T' }
|
30
|
+
backup = GithubBackup::Backup.new('ddollar', opts)
|
31
|
+
backup.client.access_token.must_equal opts[:token]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'without a token' do
|
37
|
+
|
38
|
+
it 'will not configure the client with an OAuth access token' do
|
39
|
+
opts = { :backup_root => '/tmp' }
|
40
|
+
backup = GithubBackup::Backup.new('ddollar', opts)
|
41
|
+
backup.client.access_token.must_be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe :username do
|
49
|
+
|
50
|
+
it 'returns the username of the GitHub account to back up' do
|
51
|
+
backup = GithubBackup::Backup.new('ddollar', :backup_root => '/tmp')
|
52
|
+
backup.username.must_equal 'ddollar'
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe :client do
|
58
|
+
|
59
|
+
it 'returns the Octokit client' do
|
60
|
+
backup = GithubBackup::Backup.new('ddollar', :backup_root => '/tmp')
|
61
|
+
backup.client.must_be_instance_of ::Octokit::Client
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
describe :config do
|
67
|
+
|
68
|
+
it 'returns the backup configuration' do
|
69
|
+
backup = GithubBackup::Backup.new('ddollar', :backup_root => '/tmp')
|
70
|
+
backup.config.must_be_instance_of GithubBackup::Config
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe :debug do
|
76
|
+
|
77
|
+
it 'returns false' do
|
78
|
+
backup = GithubBackup::Backup.new('ddollar', :backup_root => '/tmp')
|
79
|
+
backup.debug.must_equal false
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe GithubBackup::Config do
|
4
|
+
|
5
|
+
describe :new do
|
6
|
+
|
7
|
+
# :backup_root
|
8
|
+
it 'sets the default backup_root to the current directory' do
|
9
|
+
config = GithubBackup::Config.new
|
10
|
+
config.backup_root.must_equal Dir.pwd
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'sets the default backup_root if the option is nil' do
|
14
|
+
config = GithubBackup::Config.new(:backup_root => nil)
|
15
|
+
config.backup_root.must_equal Dir.pwd
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'sets the backup_root through an option' do
|
19
|
+
config = GithubBackup::Config.new(:backup_root => '/tmp')
|
20
|
+
config.backup_root.must_equal '/tmp'
|
21
|
+
end
|
22
|
+
|
23
|
+
# :gitconfig_path
|
24
|
+
it 'sets the default .gitconfig file path' do
|
25
|
+
config = GithubBackup::Config.new(:backup_root => '/tmp')
|
26
|
+
config.gitconfig_path.must_equal "#{ ENV['HOME'] }/.gitconfig"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'sets the default gitconfig_path if the option is nil' do
|
30
|
+
config = GithubBackup::Config.new(:gitconfig_path => nil)
|
31
|
+
config.gitconfig_path.must_equal "#{ ENV['HOME'] }/.gitconfig"
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets a .gitconfig path through an option' do
|
35
|
+
config = GithubBackup::Config.new(:backup_root => '/tmp',
|
36
|
+
:gitconfig_path => '/tmp/.gitconfig')
|
37
|
+
config.gitconfig_path.must_equal '/tmp/.gitconfig'
|
38
|
+
end
|
39
|
+
|
40
|
+
# :token
|
41
|
+
it 'sets a default value of nil for the GitHub OAuth token option' do
|
42
|
+
config = GithubBackup::Config.new(:backup_root => '/tmp')
|
43
|
+
config.token.must_be_nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'sets the GitHub OAuth token from an option key' do
|
47
|
+
config = GithubBackup::Config.new(:backup_root => '/tmp',
|
48
|
+
:token => 'S3CR3T')
|
49
|
+
config.token.must_equal 'S3CR3T'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'sets the GitHub OAuth token from .gitconfig' do
|
53
|
+
gitconfig_path = File.expand_path('../../../fixtures/gitconfig', __FILE__)
|
54
|
+
config = GithubBackup::Config.new(:backup_root => '/tmp',
|
55
|
+
:gitconfig_path => gitconfig_path)
|
56
|
+
config.token.must_equal 'SUPER_S3CR3T'
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'attributes' do
|
60
|
+
|
61
|
+
let(:config) do
|
62
|
+
GithubBackup::Config.new(:backup_root => '/tmp/backup',
|
63
|
+
:gitconfig_path => '/tmp/gitconfig',
|
64
|
+
:token => 'S3CR3T')
|
65
|
+
end
|
66
|
+
|
67
|
+
describe :backup_root do
|
68
|
+
|
69
|
+
it 'returns the directory where the repositories will be backed up' do
|
70
|
+
config.backup_root.must_equal '/tmp/backup'
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe :gitconfig_path do
|
76
|
+
|
77
|
+
it 'returns the path to a gitconfig file' do
|
78
|
+
config.gitconfig_path.must_equal '/tmp/gitconfig'
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
describe :token do
|
84
|
+
|
85
|
+
it 'returns the GitHub OAuth access token' do
|
86
|
+
config.token.must_equal 'S3CR3T'
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
describe :== do
|
94
|
+
|
95
|
+
it 'is equal if its attributes are identical' do
|
96
|
+
opts = { :backup_root => '/tmp/backup',
|
97
|
+
:gitconfig_path => '/tmp/gitconfig',
|
98
|
+
:token => 'S3CR3T'}
|
99
|
+
backup = GithubBackup::Config.new(opts)
|
100
|
+
backup.dup.must_equal backup
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'is not equal if any of the attributes vary' do
|
104
|
+
opts = { :backup_root => '/tmp/backup',
|
105
|
+
:gitconfig_path => '/tmp/gitconfig',
|
106
|
+
:token => 'S3CR3T'}
|
107
|
+
backup1 = GithubBackup::Config.new(opts)
|
108
|
+
backup2 = GithubBackup::Config.new(opts.merge(:token => 'PASSWORD'))
|
109
|
+
backup1.wont_equal backup2
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe GithubBackup::Gist do
|
4
|
+
|
5
|
+
it 'acts lie a Repository' do
|
6
|
+
GithubBackup::Gist.new(sawyer_anonymous_gist).
|
7
|
+
must_be_kind_of GithubBackup::Repository
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#clone_url' do
|
11
|
+
|
12
|
+
it 'returns the repository clone URL' do
|
13
|
+
repo = GithubBackup::Gist.new(sawyer_anonymous_gist)
|
14
|
+
repo.clone_url.must_equal 'https://gist.github.com/380919418d982afbc4fc.git'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#backup_path' do
|
20
|
+
|
21
|
+
describe 'an anonymous gist' do
|
22
|
+
|
23
|
+
it 'returns the repository backup path' do
|
24
|
+
repo = GithubBackup::Gist.new(sawyer_anonymous_gist)
|
25
|
+
repo.backup_path.must_equal 'anonymous/380919418d982afbc4fc.git'
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'a public gist' do
|
31
|
+
|
32
|
+
it 'returns the repository backup path' do
|
33
|
+
repo = GithubBackup::Gist.new(sawyer_public_gist)
|
34
|
+
repo.backup_path.must_equal 'garethrees/dae4757f11749b6edaf2.git'
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe GithubBackup::Repository do
|
4
|
+
|
5
|
+
describe '.new' do
|
6
|
+
|
7
|
+
it 'accepts a sawyer::Resource' do
|
8
|
+
repo = GithubBackup::Repository.new(sawyer_repo)
|
9
|
+
repo.sawyer_resource.must_equal sawyer_repo
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'creates a default GithubBackup::Shell instance' do
|
13
|
+
repo = GithubBackup::Repository.new(sawyer_repo)
|
14
|
+
repo.shell.must_be_instance_of GithubBackup::Shell
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#shell' do
|
20
|
+
|
21
|
+
it 'returns the shell instance' do
|
22
|
+
shell = GithubBackup::Shell.new
|
23
|
+
repo = GithubBackup::Repository.new(sawyer_repo, shell: shell)
|
24
|
+
repo.shell.must_be_same_as shell
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#backup' do
|
30
|
+
|
31
|
+
it 'clones the repository if it has not yet been backed up' do
|
32
|
+
cmd = 'git clone --mirror -n ' \
|
33
|
+
'git@github.com:ddollar/github-backup.git ' \
|
34
|
+
'/ddollar/github-backup.git'
|
35
|
+
shell = Minitest::Mock.new
|
36
|
+
shell.expect(:run, true, [cmd])
|
37
|
+
|
38
|
+
FakeFS do
|
39
|
+
repo = GithubBackup::Repository.new(sawyer_repo, shell: shell)
|
40
|
+
repo.backup(Dir.pwd)
|
41
|
+
end
|
42
|
+
|
43
|
+
FakeFS::FileSystem.clear
|
44
|
+
|
45
|
+
shell.verify
|
46
|
+
end
|
47
|
+
|
48
|
+
# TODO: This doesn't check that we change in to the backup_path before
|
49
|
+
# executing `git remote update`
|
50
|
+
it 'updates the repository if it has already been backed up' do
|
51
|
+
shell = Minitest::Mock.new
|
52
|
+
shell.expect(:run, true, ['git remote update'])
|
53
|
+
|
54
|
+
FakeFS do
|
55
|
+
repo = GithubBackup::Repository.new(sawyer_repo, shell: shell)
|
56
|
+
FileUtils.mkdir_p(File.join(Dir.pwd, repo.backup_path))
|
57
|
+
repo.backup(Dir.pwd)
|
58
|
+
end
|
59
|
+
|
60
|
+
FakeFS::FileSystem.clear
|
61
|
+
|
62
|
+
shell.verify
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#clone_url' do
|
68
|
+
|
69
|
+
it 'returns the repository clone URL' do
|
70
|
+
repo = GithubBackup::Repository.new(sawyer_repo)
|
71
|
+
repo.clone_url.must_equal 'git@github.com:ddollar/github-backup.git'
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#backup_path' do
|
77
|
+
|
78
|
+
it 'returns the repository backup path' do
|
79
|
+
repo = GithubBackup::Repository.new(sawyer_repo)
|
80
|
+
repo.backup_path.must_equal 'ddollar/github-backup.git'
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe GithubBackup::Wiki do
|
4
|
+
|
5
|
+
it 'acts lie a Repository' do
|
6
|
+
GithubBackup::Wiki.new(sawyer_repo).
|
7
|
+
must_be_kind_of GithubBackup::Repository
|
8
|
+
end
|
9
|
+
|
10
|
+
describe '#clone_url' do
|
11
|
+
|
12
|
+
it 'returns the repository clone URL' do
|
13
|
+
repo = GithubBackup::Wiki.new(sawyer_repo)
|
14
|
+
repo.clone_url.must_equal 'https://github.com/ddollar/github-backup.wiki.git'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#backup_path' do
|
20
|
+
|
21
|
+
it 'returns the repository backup path' do
|
22
|
+
repo = GithubBackup::Wiki.new(sawyer_repo)
|
23
|
+
repo.backup_path.must_equal 'ddollar/github-backup.wiki.git'
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
|
3
|
+
require 'github-backup'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
require 'fakefs/safe'
|
6
|
+
require File.join(File.dirname(__FILE__), 'support', 'deep_struct')
|
7
|
+
|
8
|
+
def sawyer_repo
|
9
|
+
DeepStruct.new(
|
10
|
+
{:id=>251112,
|
11
|
+
:name=>"github-backup",
|
12
|
+
:full_name=>"ddollar/github-backup",
|
13
|
+
:owner=>
|
14
|
+
{:login=>"ddollar",
|
15
|
+
:id=>3308,
|
16
|
+
:avatar_url=>"https://avatars.githubusercontent.com/u/3308?v=3",
|
17
|
+
:gravatar_id=>"",
|
18
|
+
:url=>"https://api.github.com/users/ddollar",
|
19
|
+
:html_url=>"https://github.com/ddollar",
|
20
|
+
:followers_url=>"https://api.github.com/users/ddollar/followers",
|
21
|
+
:following_url=>
|
22
|
+
"https://api.github.com/users/ddollar/following{/other_user}",
|
23
|
+
:gists_url=>"https://api.github.com/users/ddollar/gists{/gist_id}",
|
24
|
+
:starred_url=>"https://api.github.com/users/ddollar/starred{/owner}{/repo}",
|
25
|
+
:subscriptions_url=>"https://api.github.com/users/ddollar/subscriptions",
|
26
|
+
:organizations_url=>"https://api.github.com/users/ddollar/orgs",
|
27
|
+
:repos_url=>"https://api.github.com/users/ddollar/repos",
|
28
|
+
:events_url=>"https://api.github.com/users/ddollar/events{/privacy}",
|
29
|
+
:received_events_url=>
|
30
|
+
"https://api.github.com/users/ddollar/received_events",
|
31
|
+
:type=>"User",
|
32
|
+
:site_admin=>false},
|
33
|
+
:private=>false,
|
34
|
+
:html_url=>"https://github.com/ddollar/github-backup",
|
35
|
+
:description=>"Back up your Github repositories",
|
36
|
+
:fork=>false,
|
37
|
+
:url=>"https://api.github.com/repos/ddollar/github-backup",
|
38
|
+
:forks_url=>"https://api.github.com/repos/ddollar/github-backup/forks",
|
39
|
+
:keys_url=>"https://api.github.com/repos/ddollar/github-backup/keys{/key_id}",
|
40
|
+
:collaborators_url=>
|
41
|
+
"https://api.github.com/repos/ddollar/github-backup/collaborators{/collaborator}",
|
42
|
+
:teams_url=>"https://api.github.com/repos/ddollar/github-backup/teams",
|
43
|
+
:hooks_url=>"https://api.github.com/repos/ddollar/github-backup/hooks",
|
44
|
+
:issue_events_url=>
|
45
|
+
"https://api.github.com/repos/ddollar/github-backup/issues/events{/number}",
|
46
|
+
:events_url=>"https://api.github.com/repos/ddollar/github-backup/events",
|
47
|
+
:assignees_url=>
|
48
|
+
"https://api.github.com/repos/ddollar/github-backup/assignees{/user}",
|
49
|
+
:branches_url=>
|
50
|
+
"https://api.github.com/repos/ddollar/github-backup/branches{/branch}",
|
51
|
+
:tags_url=>"https://api.github.com/repos/ddollar/github-backup/tags",
|
52
|
+
:blobs_url=>
|
53
|
+
"https://api.github.com/repos/ddollar/github-backup/git/blobs{/sha}",
|
54
|
+
:git_tags_url=>
|
55
|
+
"https://api.github.com/repos/ddollar/github-backup/git/tags{/sha}",
|
56
|
+
:git_refs_url=>
|
57
|
+
"https://api.github.com/repos/ddollar/github-backup/git/refs{/sha}",
|
58
|
+
:trees_url=>
|
59
|
+
"https://api.github.com/repos/ddollar/github-backup/git/trees{/sha}",
|
60
|
+
:statuses_url=>
|
61
|
+
"https://api.github.com/repos/ddollar/github-backup/statuses/{sha}",
|
62
|
+
:languages_url=>
|
63
|
+
"https://api.github.com/repos/ddollar/github-backup/languages",
|
64
|
+
:stargazers_url=>
|
65
|
+
"https://api.github.com/repos/ddollar/github-backup/stargazers",
|
66
|
+
:contributors_url=>
|
67
|
+
"https://api.github.com/repos/ddollar/github-backup/contributors",
|
68
|
+
:subscribers_url=>
|
69
|
+
"https://api.github.com/repos/ddollar/github-backup/subscribers",
|
70
|
+
:subscription_url=>
|
71
|
+
"https://api.github.com/repos/ddollar/github-backup/subscription",
|
72
|
+
:commits_url=>
|
73
|
+
"https://api.github.com/repos/ddollar/github-backup/commits{/sha}",
|
74
|
+
:git_commits_url=>
|
75
|
+
"https://api.github.com/repos/ddollar/github-backup/git/commits{/sha}",
|
76
|
+
:comments_url=>
|
77
|
+
"https://api.github.com/repos/ddollar/github-backup/comments{/number}",
|
78
|
+
:issue_comment_url=>
|
79
|
+
"https://api.github.com/repos/ddollar/github-backup/issues/comments{/number}",
|
80
|
+
:contents_url=>
|
81
|
+
"https://api.github.com/repos/ddollar/github-backup/contents/{+path}",
|
82
|
+
:compare_url=>
|
83
|
+
"https://api.github.com/repos/ddollar/github-backup/compare/{base}...{head}",
|
84
|
+
:merges_url=>"https://api.github.com/repos/ddollar/github-backup/merges",
|
85
|
+
:archive_url=>
|
86
|
+
"https://api.github.com/repos/ddollar/github-backup/{archive_format}{/ref}",
|
87
|
+
:downloads_url=>
|
88
|
+
"https://api.github.com/repos/ddollar/github-backup/downloads",
|
89
|
+
:issues_url=>
|
90
|
+
"https://api.github.com/repos/ddollar/github-backup/issues{/number}",
|
91
|
+
:pulls_url=>
|
92
|
+
"https://api.github.com/repos/ddollar/github-backup/pulls{/number}",
|
93
|
+
:milestones_url=>
|
94
|
+
"https://api.github.com/repos/ddollar/github-backup/milestones{/number}",
|
95
|
+
:notifications_url=>
|
96
|
+
"https://api.github.com/repos/ddollar/github-backup/notifications{?since,all,participating}",
|
97
|
+
:labels_url=>
|
98
|
+
"https://api.github.com/repos/ddollar/github-backup/labels{/name}",
|
99
|
+
:releases_url=>
|
100
|
+
"https://api.github.com/repos/ddollar/github-backup/releases{/id}",
|
101
|
+
:created_at=>"2009-07-14 17:52:13 UTC",
|
102
|
+
:updated_at=>"2015-08-27 14:54:56 UTC",
|
103
|
+
:pushed_at=>"2015-06-13 15:58:00 UTC",
|
104
|
+
:git_url=>"git://github.com/ddollar/github-backup.git",
|
105
|
+
:ssh_url=>"git@github.com:ddollar/github-backup.git",
|
106
|
+
:clone_url=>"https://github.com/ddollar/github-backup.git",
|
107
|
+
:svn_url=>"https://github.com/ddollar/github-backup",
|
108
|
+
:homepage=>"",
|
109
|
+
:size=>306,
|
110
|
+
:stargazers_count=>66,
|
111
|
+
:watchers_count=>66,
|
112
|
+
:language=>"Ruby",
|
113
|
+
:has_issues=>true,
|
114
|
+
:has_downloads=>true,
|
115
|
+
:has_wiki=>true,
|
116
|
+
:has_pages=>false,
|
117
|
+
:forks_count=>18,
|
118
|
+
:mirror_url=>nil,
|
119
|
+
:open_issues_count=>0,
|
120
|
+
:forks=>18,
|
121
|
+
:open_issues=>0,
|
122
|
+
:watchers=>66,
|
123
|
+
:default_branch=>"master",
|
124
|
+
:permissions=>{:admin=>false, :push=>true, :pull=>true},
|
125
|
+
:network_count=>18,
|
126
|
+
:subscribers_count=>9})
|
127
|
+
end
|
128
|
+
|
129
|
+
def sawyer_anonymous_gist
|
130
|
+
DeepStruct.new(
|
131
|
+
{:url=>"https://api.github.com/gists/380919418d982afbc4fc",
|
132
|
+
:forks_url=>"https://api.github.com/gists/380919418d982afbc4fc/forks",
|
133
|
+
:commits_url=>"https://api.github.com/gists/380919418d982afbc4fc/commits",
|
134
|
+
:id=>"380919418d982afbc4fc",
|
135
|
+
:git_pull_url=>"https://gist.github.com/380919418d982afbc4fc.git",
|
136
|
+
:git_push_url=>"https://gist.github.com/380919418d982afbc4fc.git",
|
137
|
+
:html_url=>"https://gist.github.com/380919418d982afbc4fc",
|
138
|
+
:files=>
|
139
|
+
{:"anon.txt"=>
|
140
|
+
{:filename=>"anon.txt",
|
141
|
+
:type=>"text/plain",
|
142
|
+
:language=>"Text",
|
143
|
+
:raw_url=>
|
144
|
+
"https://gist.githubusercontent.com/anonymous/380919418d982afbc4fc/raw/6991197cadbb876aabebdaa9ead52962857f0443/anon.txt",
|
145
|
+
:size=>19,
|
146
|
+
:truncated=>false,
|
147
|
+
:content=>"Nothing to see here"}},
|
148
|
+
:public=>true,
|
149
|
+
:created_at=>'2015-09-07 22:23:58 UTC',
|
150
|
+
:updated_at=>'2015-09-07 22:23:58 UTC',
|
151
|
+
:description=>"Anonymous Gist",
|
152
|
+
:comments=>0,
|
153
|
+
:user=>nil,
|
154
|
+
:comments_url=>"https://api.github.com/gists/380919418d982afbc4fc/comments",
|
155
|
+
:forks=>[],
|
156
|
+
:history=>
|
157
|
+
[{:user=>
|
158
|
+
{:login=>"invalid-email-address",
|
159
|
+
:id=>148100,
|
160
|
+
:avatar_url=>"https://avatars.githubusercontent.com/u/148100?v=3",
|
161
|
+
:gravatar_id=>"",
|
162
|
+
:url=>"https://api.github.com/users/invalid-email-address",
|
163
|
+
:html_url=>"https://github.com/invalid-email-address",
|
164
|
+
:followers_url=>
|
165
|
+
"https://api.github.com/users/invalid-email-address/followers",
|
166
|
+
:following_url=>
|
167
|
+
"https://api.github.com/users/invalid-email-address/following{/other_user}",
|
168
|
+
:gists_url=>
|
169
|
+
"https://api.github.com/users/invalid-email-address/gists{/gist_id}",
|
170
|
+
:starred_url=>
|
171
|
+
"https://api.github.com/users/invalid-email-address/starred{/owner}{/repo}",
|
172
|
+
:subscriptions_url=>
|
173
|
+
"https://api.github.com/users/invalid-email-address/subscriptions",
|
174
|
+
:organizations_url=>
|
175
|
+
"https://api.github.com/users/invalid-email-address/orgs",
|
176
|
+
:repos_url=>"https://api.github.com/users/invalid-email-address/repos",
|
177
|
+
:events_url=>
|
178
|
+
"https://api.github.com/users/invalid-email-address/events{/privacy}",
|
179
|
+
:received_events_url=>
|
180
|
+
"https://api.github.com/users/invalid-email-address/received_events",
|
181
|
+
:type=>"User",
|
182
|
+
:site_admin=>false},
|
183
|
+
:version=>"496b2ba699998442fbf864e9e7dcf47165b2f478",
|
184
|
+
:committed_at=>'2015-09-07 22:23:58 UTC',
|
185
|
+
:change_status=>{:total=>1, :additions=>1, :deletions=>0},
|
186
|
+
:url=>
|
187
|
+
"https://api.github.com/gists/380919418d982afbc4fc/496b2ba699998442fbf864e9e7dcf47165b2f478"}]}
|
188
|
+
)
|
189
|
+
end
|
190
|
+
|
191
|
+
def sawyer_public_gist
|
192
|
+
DeepStruct.new(
|
193
|
+
{:url=>"https://api.github.com/gists/dae4757f11749b6edaf2",
|
194
|
+
:forks_url=>"https://api.github.com/gists/dae4757f11749b6edaf2/forks",
|
195
|
+
:commits_url=>"https://api.github.com/gists/dae4757f11749b6edaf2/commits",
|
196
|
+
:id=>"dae4757f11749b6edaf2",
|
197
|
+
:git_pull_url=>"https://gist.github.com/dae4757f11749b6edaf2.git",
|
198
|
+
:git_push_url=>"https://gist.github.com/dae4757f11749b6edaf2.git",
|
199
|
+
:html_url=>"https://gist.github.com/dae4757f11749b6edaf2",
|
200
|
+
:files=>
|
201
|
+
{:"test.txt"=>
|
202
|
+
{:filename=>"test.txt",
|
203
|
+
:type=>"text/plain",
|
204
|
+
:language=>"Text",
|
205
|
+
:raw_url=>
|
206
|
+
"https://gist.githubusercontent.com/garethrees/dae4757f11749b6edaf2/raw/3fde4e20239e009fb18787483735352a778229da/test.txt",
|
207
|
+
:size=>5,
|
208
|
+
:truncated=>false,
|
209
|
+
:content=>"1,2,3"}},
|
210
|
+
:public=>true,
|
211
|
+
:created_at=>'2015-09-07 22:34:59 UTC',
|
212
|
+
:updated_at=>'2015-09-07 22:34:59 UTC',
|
213
|
+
:description=>"Test Gist",
|
214
|
+
:comments=>0,
|
215
|
+
:user=>nil,
|
216
|
+
:comments_url=>"https://api.github.com/gists/dae4757f11749b6edaf2/comments",
|
217
|
+
:owner=>
|
218
|
+
{:login=>"garethrees",
|
219
|
+
:id=>282788,
|
220
|
+
:avatar_url=>"https://avatars.githubusercontent.com/u/282788?v=3",
|
221
|
+
:gravatar_id=>"",
|
222
|
+
:url=>"https://api.github.com/users/garethrees",
|
223
|
+
:html_url=>"https://github.com/garethrees",
|
224
|
+
:followers_url=>"https://api.github.com/users/garethrees/followers",
|
225
|
+
:following_url=>
|
226
|
+
"https://api.github.com/users/garethrees/following{/other_user}",
|
227
|
+
:gists_url=>"https://api.github.com/users/garethrees/gists{/gist_id}",
|
228
|
+
:starred_url=>
|
229
|
+
"https://api.github.com/users/garethrees/starred{/owner}{/repo}",
|
230
|
+
:subscriptions_url=>"https://api.github.com/users/garethrees/subscriptions",
|
231
|
+
:organizations_url=>"https://api.github.com/users/garethrees/orgs",
|
232
|
+
:repos_url=>"https://api.github.com/users/garethrees/repos",
|
233
|
+
:events_url=>"https://api.github.com/users/garethrees/events{/privacy}",
|
234
|
+
:received_events_url=>
|
235
|
+
"https://api.github.com/users/garethrees/received_events",
|
236
|
+
:type=>"User",
|
237
|
+
:site_admin=>false},
|
238
|
+
:forks=>[],
|
239
|
+
:history=>
|
240
|
+
[{:user=>
|
241
|
+
{:login=>"garethrees",
|
242
|
+
:id=>282788,
|
243
|
+
:avatar_url=>"https://avatars.githubusercontent.com/u/282788?v=3",
|
244
|
+
:gravatar_id=>"",
|
245
|
+
:url=>"https://api.github.com/users/garethrees",
|
246
|
+
:html_url=>"https://github.com/garethrees",
|
247
|
+
:followers_url=>"https://api.github.com/users/garethrees/followers",
|
248
|
+
:following_url=>
|
249
|
+
"https://api.github.com/users/garethrees/following{/other_user}",
|
250
|
+
:gists_url=>"https://api.github.com/users/garethrees/gists{/gist_id}",
|
251
|
+
:starred_url=>
|
252
|
+
"https://api.github.com/users/garethrees/starred{/owner}{/repo}",
|
253
|
+
:subscriptions_url=>
|
254
|
+
"https://api.github.com/users/garethrees/subscriptions",
|
255
|
+
:organizations_url=>"https://api.github.com/users/garethrees/orgs",
|
256
|
+
:repos_url=>"https://api.github.com/users/garethrees/repos",
|
257
|
+
:events_url=>"https://api.github.com/users/garethrees/events{/privacy}",
|
258
|
+
:received_events_url=>
|
259
|
+
"https://api.github.com/users/garethrees/received_events",
|
260
|
+
:type=>"User",
|
261
|
+
:site_admin=>false},
|
262
|
+
:version=>"99e263bff14f9565b844f7913b6b505513308a5f",
|
263
|
+
:committed_at=>'2015-09-07 22:34:59 UTC',
|
264
|
+
:change_status=>{:total=>1, :additions=>1, :deletions=>0},
|
265
|
+
:url=>
|
266
|
+
"https://api.github.com/gists/dae4757f11749b6edaf2/99e263bff14f9565b844f7913b6b505513308a5f"}]})
|
267
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
class DeepStruct < OpenStruct
|
4
|
+
def initialize(hash=nil)
|
5
|
+
@table = {}
|
6
|
+
@hash_table = {}
|
7
|
+
|
8
|
+
if hash
|
9
|
+
hash.each do |k,v|
|
10
|
+
@table[k.to_sym] = (v.is_a?(Hash) ? self.class.new(v) : v)
|
11
|
+
@hash_table[k.to_sym] = v
|
12
|
+
|
13
|
+
new_ostruct_member(k)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
@hash_table
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: github-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Dollar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-06-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: octokit
|
@@ -24,6 +24,48 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fakefs
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.6.7
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.6.7
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.5.0
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 5.5.0
|
27
69
|
description: Library and command line tool for backing up GitHub repositories
|
28
70
|
email: david@dollar.io
|
29
71
|
executables:
|
@@ -33,17 +75,32 @@ extra_rdoc_files: []
|
|
33
75
|
files:
|
34
76
|
- ".document"
|
35
77
|
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
36
79
|
- Gemfile
|
37
80
|
- Gemfile.lock
|
38
81
|
- LICENSE
|
39
82
|
- Makefile
|
40
83
|
- README.md
|
84
|
+
- Rakefile
|
41
85
|
- bin/github-backup
|
42
86
|
- github-backup.gemspec
|
43
87
|
- lib/github-backup.rb
|
44
88
|
- lib/github-backup/backup.rb
|
45
89
|
- lib/github-backup/config.rb
|
90
|
+
- lib/github-backup/gist.rb
|
91
|
+
- lib/github-backup/github_repository_collection.rb
|
92
|
+
- lib/github-backup/repository.rb
|
93
|
+
- lib/github-backup/shell.rb
|
46
94
|
- lib/github-backup/version.rb
|
95
|
+
- lib/github-backup/wiki.rb
|
96
|
+
- spec/fixtures/gitconfig
|
97
|
+
- spec/lib/github-backup/backup_spec.rb
|
98
|
+
- spec/lib/github-backup/config_spec.rb
|
99
|
+
- spec/lib/github-backup/gist_spec.rb
|
100
|
+
- spec/lib/github-backup/repository_spec.rb
|
101
|
+
- spec/lib/github-backup/wiki_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
- spec/support/deep_struct.rb
|
47
104
|
homepage: http://github.com/ddollar/github-backup
|
48
105
|
licenses:
|
49
106
|
- MIT
|
@@ -64,9 +121,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
121
|
version: '0'
|
65
122
|
requirements: []
|
66
123
|
rubyforge_project:
|
67
|
-
rubygems_version: 2.
|
124
|
+
rubygems_version: 2.7.3
|
68
125
|
signing_key:
|
69
126
|
specification_version: 4
|
70
127
|
summary: Backup your Github repositories
|
71
128
|
test_files: []
|
72
|
-
has_rdoc:
|