github-backup 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/ddollar/github-backup.svg?branch=master)](https://travis-ci.org/ddollar/github-backup)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/ddollar/github-backup/badges/gpa.svg)](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:
|