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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5369c3068e82f9ef8466455c2216ccd68d6c5b50
4
- data.tar.gz: 06f0366d478b651c8ec6a160d0502af6e2e58b41
2
+ SHA256:
3
+ metadata.gz: 7cbe362a6dd02d1e857cb3fabec20bbe633a71540a1c155fb98d1d73fc7d956a
4
+ data.tar.gz: 159a68f3e3b2b66f720475cbb86de2c582d02e837751c90a2708f79047907769
5
5
  SHA512:
6
- metadata.gz: 6723ef4b7331f84535277994b9f9bb627d9a91764f0945477a521d0f0ad6e4bf1c885b5c41bc98d591ddc30ffb2522a2e8eeb363e05f1167cdf4989baf0db073
7
- data.tar.gz: 9996e98b8d954e0b96f17f6a15c28920194cc177692820fd82787f0e9770c4cffe9d826323da74c0c67e056266b2f9b1a51ac7d425cd97dd45742766f93cd032
6
+ metadata.gz: 56ba187b5dbae8b4a2321d1ed70d7ce8a3cd680257587da202f4a67297222534c1549dfabe0c494109fc6d1c9404ace3d918a43e9c56f6eb5a0c83033178016d
7
+ data.tar.gz: b7a171b16b6bf0d825df73be89baba07c7d26c27165264cdb7e345f248b480afc4f33a0b8e1ea9bce84948e2f31b3c62dcf06230564b6336e0f0127ebf2afddf
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.5.0
4
+ - 2.4.0
5
+ sudo: false
@@ -1,18 +1,21 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- github-backup (0.10.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.6)
11
- faraday (0.9.0)
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.7.0)
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
- ## github-backup
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
 
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.test_files = FileList['spec/lib/github-backup/*_spec.rb']
5
+ t.verbose = true
6
+ end
7
+
8
+ task :default => :test
@@ -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 == 2
30
+ unless ARGV.length >= 1
19
31
  puts optionparser.help
20
32
  exit 1
21
33
  end
@@ -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
@@ -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 = username
7
- @debug = false
8
- @config = Config.new(options)
9
- @client = Octokit::Client.new(:access_token => config.token)
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
- FileUtils::mkdir_p(backup_root)
33
- repositories.each do |repository|
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
- # Iterate over paginated response
44
- last_response = client.last_response
45
- unless last_response.rels[:next].nil?
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
- repos
54
- end
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
- def backup_repository(repository)
67
- if File.exists?(backup_directory_for(repository))
68
- backup_repository_incremental(repository)
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 backup_repository_initial(repository)
75
- FileUtils::cd(backup_root) do
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 username_is_organisation?
87
- client.user(username).type == 'Organization'
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 shell(command)
96
- puts "EXECUTING: #{command}" if debug
97
- IO.popen(command, 'r') do |io|
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
@@ -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) { raise ArgumentError, 'A backup_root option is required' }
12
- @gitconfig_path = options.fetch(:gitconfig_path) { DEFAULTS[:gitconfig_path] }
13
- @token = options.fetch(:token) { default_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.exists?(gitconfig_path)
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
@@ -1,3 +1,3 @@
1
1
  module GithubBackup
2
- VERSION = "0.10.0"
2
+ VERSION = "0.11.0"
3
3
  end
@@ -0,0 +1,12 @@
1
+ require_relative './repository'
2
+ module GithubBackup
3
+ class Wiki < Repository
4
+ def clone_url
5
+ "https://github.com/#{ sawyer_resource.full_name }.wiki.git"
6
+ end
7
+
8
+ def backup_path
9
+ "#{ sawyer_resource.full_name }.wiki.git"
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,2 @@
1
+ [github]
2
+ token=SUPER_S3CR3T
@@ -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
@@ -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.10.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: 2015-01-04 00:00:00.000000000 Z
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.2.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: