gitbak 0.4.3 → 0.5.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 +7 -0
- data/README.md +40 -56
- data/gitbak.gemspec +3 -5
- data/lib/gitbak.rb +79 -70
- data/lib/gitbak/exec.rb +17 -28
- data/lib/gitbak/misc.rb +10 -26
- data/lib/gitbak/services.rb +6 -147
- data/lib/gitbak/services/bitbucket.rb +85 -0
- data/lib/gitbak/services/gist.rb +80 -0
- data/lib/gitbak/services/github.rb +83 -0
- data/lib/gitbak/version.rb +2 -2
- metadata +19 -39
- data/lib/gitbak/config.rb +0 -112
- data/lib/gitbak/eval.rb +0 -6
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3e3a5e41025ca2744190c8babdc5bac3247a007b
|
4
|
+
data.tar.gz: 65f8655194b5bf74dbc6d5e488e166f6870bc700
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ddee8e238c06d24f9db78c23c2b896d886b9ab29bda530d449f6a6e6b6698cded0b86919aafb7bbde3e079bf710b06d6a1788a8b651815a41584221856e4ffcb
|
7
|
+
data.tar.gz: 92627c7bd07aeaf46896e92d40a0567815d5c216f577ca6571c45311654dd9f3ada2cc2230ea348c8377085b418b93c414de693f935762a0bb13aed8c7cb6552
|
data/README.md
CHANGED
@@ -1,16 +1,15 @@
|
|
1
|
-
|
1
|
+
[]: {{{1
|
2
2
|
|
3
3
|
File : README
|
4
4
|
Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
Date :
|
5
|
+
Date : 2014-02-19
|
6
6
|
|
7
|
-
Copyright : Copyright (C)
|
8
|
-
Version : v0.
|
7
|
+
Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
Version : v0.5.0
|
9
9
|
|
10
|
-
|
10
|
+
[]: }}}1
|
11
11
|
|
12
12
|
## Description
|
13
|
-
<!-- \{{{1 -->
|
14
13
|
|
15
14
|
gitbak - bitbucket/github/gist backup
|
16
15
|
|
@@ -19,70 +18,63 @@
|
|
19
18
|
|
20
19
|
When run, gitbak:
|
21
20
|
|
22
|
-
* asks for
|
21
|
+
* asks for passwords;
|
23
22
|
* lists repositories using APIs - authenticating if necessary;
|
24
23
|
* clones/updates repositories;
|
25
24
|
* shows a summary (if verbose)
|
26
25
|
|
27
|
-
<!-- }}}1 -->
|
28
|
-
|
29
26
|
## Usage
|
30
|
-
<!-- \{{{1 -->
|
31
|
-
|
32
|
-
$ gitbak --help # show options
|
33
|
-
$ vim ~/.gitbak # configure
|
34
|
-
$ gitbak -v # mirror
|
35
|
-
$ time gitbak -v 2>&1 | tee log # w/ logfile
|
36
|
-
|
37
|
-
You may want to run gitbak as a cron job. TODO
|
38
27
|
|
39
|
-
|
28
|
+
```bash
|
29
|
+
$ gitbak --help # show options
|
30
|
+
$ vim ~/.gitbak # configure
|
31
|
+
$ gitbak --no-act # dry run
|
32
|
+
$ gitbak -v # mirror
|
33
|
+
$ time gitbak -v 2>&1 | tee "$(date +%Y%m%d)".log # w/ logfile
|
34
|
+
```
|
40
35
|
|
41
36
|
## Installing
|
42
|
-
<!-- \{{{1 -->
|
43
37
|
|
44
|
-
|
38
|
+
```bash
|
39
|
+
$ gem install gitbak # rubygems
|
40
|
+
```
|
45
41
|
|
46
42
|
Get it at https://github.com/obfusk/gitbak. Depends: git, ruby.
|
47
43
|
|
48
|
-
<!-- }}}1 -->
|
49
|
-
|
50
44
|
## Configuration
|
51
|
-
<!-- \{{{1 -->
|
52
45
|
|
53
46
|
### Example
|
54
47
|
|
55
48
|
```ruby
|
56
49
|
# ~/.gitbak
|
57
|
-
|
58
50
|
dir = "#{ Dir.home }/__mirror__/#{ Time.new.strftime '%Y%m%d' }"
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
services.github "#{dir}/#{u}/github" , u, auth: true
|
64
|
-
services.gist "#{dir}/#{u}/gist" , u, auth: true
|
65
|
-
end
|
51
|
+
%w{ bob alice }.each do |u|
|
52
|
+
bitbucket "#{dir}/#{u}/bitbucket", user: u
|
53
|
+
github "#{dir}/#{u}/github" , user: u, token: true
|
54
|
+
gist "#{dir}/#{u}/gist" , user: u, auth: :github
|
66
55
|
end
|
67
56
|
```
|
68
57
|
|
69
58
|
### Methods
|
70
59
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
#### Repository Options
|
60
|
+
```ruby
|
61
|
+
bitbucket repo #, options...
|
62
|
+
github repo #, options...
|
63
|
+
gist repo #, options...
|
64
|
+
```
|
78
65
|
|
79
|
-
|
80
|
-
:method defaults to :ssh.
|
66
|
+
### Options
|
81
67
|
|
82
|
-
|
68
|
+
```ruby
|
69
|
+
user: "username" # mandatory
|
70
|
+
token: true # use token instead of user/pass (default: false)
|
71
|
+
auth: false # use authentication (default: true);
|
72
|
+
use :github w/ gist to re-use github auth
|
73
|
+
method: :https # clone method (default: :ssh);
|
74
|
+
NB: https auth not implemented yet
|
75
|
+
```
|
83
76
|
|
84
77
|
## TODO
|
85
|
-
<!-- \{{{1 -->
|
86
78
|
|
87
79
|
Some things that may be useful/implemented at some point.
|
88
80
|
|
@@ -90,7 +82,7 @@ end
|
|
90
82
|
* tests?
|
91
83
|
* better error handling?
|
92
84
|
|
93
|
-
|
85
|
+
#
|
94
86
|
|
95
87
|
* custom services (should be easy to add already)
|
96
88
|
* metadata (issues, wikis, ...)
|
@@ -99,26 +91,18 @@ end
|
|
99
91
|
* filtering
|
100
92
|
* oauth?
|
101
93
|
|
102
|
-
|
94
|
+
#
|
103
95
|
|
104
96
|
* specify ssh key(s)?
|
105
97
|
* https clone auth?
|
106
98
|
|
107
|
-
<!-- }}}1 -->
|
108
|
-
|
109
99
|
## License
|
110
|
-
<!-- \{{{1 -->
|
111
100
|
|
112
|
-
|
113
|
-
|
114
|
-
<!-- }}}1 -->
|
101
|
+
GPLv3+ [1].
|
115
102
|
|
116
103
|
## References
|
117
|
-
<!-- \{{{1 -->
|
118
|
-
|
119
|
-
[1] GNU General Public License, version 2
|
120
|
-
--- http://www.opensource.org/licenses/GPL-2.0
|
121
104
|
|
122
|
-
|
105
|
+
[1] GNU General Public License, version 3
|
106
|
+
--- http://www.gnu.org/licenses/gpl-3.0.html
|
123
107
|
|
124
|
-
|
108
|
+
[]: ! ( vim: set tw=70 sw=2 sts=2 et fdm=marker : )
|
data/gitbak.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
|
12
12
|
When run, gitbak:
|
13
13
|
|
14
|
-
* asks for
|
14
|
+
* asks for passwords;
|
15
15
|
* lists repositories using APIs - authenticating if necessary;
|
16
16
|
* clones/updates repositories;
|
17
17
|
* shows a summary (if verbose)
|
@@ -23,13 +23,11 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.authors = [ 'Felix C. Stegerman' ]
|
24
24
|
s.email = %w{ flx@obfusk.net }
|
25
25
|
|
26
|
-
s.license = '
|
26
|
+
s.license = 'GPLv3+'
|
27
27
|
|
28
28
|
s.executables = %w{ gitbak }
|
29
|
-
s.files = %w{ .yardopts README.md
|
29
|
+
s.files = %w{ .yardopts README.md gitbak.gemspec } \
|
30
30
|
+ Dir['lib/**/*.rb']
|
31
31
|
|
32
|
-
s.add_runtime_dependency 'json'
|
33
|
-
|
34
32
|
s.required_ruby_version = '>= 1.9.1'
|
35
33
|
end
|
data/lib/gitbak.rb
CHANGED
@@ -2,41 +2,47 @@
|
|
2
2
|
#
|
3
3
|
# File : gitbak.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date :
|
5
|
+
# Date : 2014-02-19
|
6
6
|
#
|
7
|
-
# Copyright : Copyright (C)
|
8
|
-
# Licence :
|
7
|
+
# Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
# Licence : GPLv3+
|
9
9
|
#
|
10
10
|
# -- ; }}}1
|
11
11
|
|
12
12
|
require 'gitbak/misc'
|
13
|
-
require 'gitbak/services'
|
14
|
-
require 'gitbak/version'
|
15
13
|
|
16
14
|
require 'fileutils'
|
15
|
+
require 'json'
|
16
|
+
require 'open-uri'
|
17
17
|
|
18
18
|
# --
|
19
19
|
|
20
20
|
# gitbak namespace
|
21
21
|
module GitBak
|
22
22
|
|
23
|
+
module Services; end
|
24
|
+
|
25
|
+
# authentication error
|
26
|
+
class AuthError < Error; end
|
27
|
+
|
28
|
+
SERVICES = {}
|
29
|
+
|
30
|
+
# --
|
31
|
+
|
23
32
|
# extract name from remote; e.g. "git@server:foo/bar.git" and
|
24
33
|
# "https://server/foo/bar.git" become "bar"
|
25
|
-
def self.repo_name
|
34
|
+
def self.repo_name(remote)
|
26
35
|
remote.sub(%r!^.*[/:]!, '').sub(/\.git$/, '')
|
27
36
|
end
|
28
37
|
|
29
|
-
# clone (from remote) or update repository (in dir)
|
38
|
+
# clone (from remote) or update repository (in dir), optionally
|
30
39
|
# verbose
|
31
|
-
def self.mirror_repo
|
40
|
+
def self.mirror_repo(verbose, noact, remote, dir) # {{{1
|
32
41
|
name = repo_name remote
|
33
42
|
name_ = name + '.git'
|
34
43
|
repo_dir = "#{dir}/#{name_}"
|
35
|
-
|
36
|
-
sys = ->(args) { Misc.sys *args, verbose: verbose, noact: noact }
|
37
|
-
|
44
|
+
sys = -> args { Misc.sys *args, verbose: verbose, noact: noact }
|
38
45
|
FileUtils.mkdir_p dir
|
39
|
-
|
40
46
|
if Misc.exists? repo_dir
|
41
47
|
puts "$ cd #{repo_dir}" if verbose or noact
|
42
48
|
FileUtils.cd(repo_dir) do
|
@@ -52,79 +58,82 @@ module GitBak
|
|
52
58
|
|
53
59
|
# --
|
54
60
|
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
# get data from API, optionally w/ auth
|
62
|
+
# @raise AuthError on 401
|
63
|
+
def self.api_get(url, opts, info, auth, f) # {{{1
|
64
|
+
g = f || -> x { x }
|
65
|
+
o = auth ? { http_basic_authentication:
|
66
|
+
[auth[:user],auth[:pass]] } : {}
|
67
|
+
begin
|
68
|
+
g[open url, o.merge(opts || {})]
|
69
|
+
rescue OpenURI::HTTPError => e
|
70
|
+
if e.io.status[0] == '401'
|
71
|
+
raise AuthError, "401 for #{auth[:user]} #{info}"
|
72
|
+
else
|
73
|
+
raise
|
64
74
|
end
|
65
75
|
end
|
76
|
+
end # }}}1
|
66
77
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
78
|
+
# get paginated data from API
|
79
|
+
def self.paginate(url, opts, info, auth, f, pag, join)
|
80
|
+
pages = []; g = -> { api_get url, opts, info, auth, f }
|
81
|
+
while url; pages << data = g[]; url = pag[data]; end
|
82
|
+
join[pages]
|
83
|
+
end
|
74
84
|
|
75
|
-
|
76
|
-
|
85
|
+
# concatenate json
|
86
|
+
def self.cat_json(pages)
|
87
|
+
pages.map { |x| JSON.load x } .flatten 1
|
88
|
+
end
|
89
|
+
|
90
|
+
# --
|
77
91
|
|
78
|
-
#
|
79
|
-
def self.
|
80
|
-
|
81
|
-
|
82
|
-
cfgs.map do |cfg|
|
83
|
-
puts "listing #{service}/#{cfg[:user]} ..." if verbose
|
92
|
+
# show info about mirroring
|
93
|
+
def self.show_mirror(*args)
|
94
|
+
puts "==> #{args*' | '} <=="
|
95
|
+
end
|
84
96
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
97
|
+
# show info about number of repos
|
98
|
+
def self.show_summary(info, n)
|
99
|
+
printf " %3d | %s\n", n, info
|
100
|
+
end
|
90
101
|
|
91
|
-
|
92
|
-
end
|
93
|
-
end .flatten 1
|
94
|
-
end # }}}1
|
102
|
+
# --
|
95
103
|
|
96
|
-
#
|
97
|
-
def self.
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
mirror_repo verbose, noact, r[:remote], dir
|
103
|
-
puts if verbose
|
104
|
-
end
|
104
|
+
# configure
|
105
|
+
def self.configure(file) # {{{1
|
106
|
+
c = {}; x = Object.new
|
107
|
+
x.instance_eval { def self._binding; binding; end }
|
108
|
+
SERVICES.each_pair do |k,v|
|
109
|
+
x.define_singleton_method(k) { |*a| v.configure c, *a }
|
105
110
|
end
|
111
|
+
x._binding.eval File.read(file), file; c
|
106
112
|
end # }}}1
|
107
113
|
|
108
|
-
#
|
109
|
-
def self.
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
+
# run!
|
115
|
+
def self.main(verbose, noact, config) # {{{1
|
116
|
+
SERVICES.each_pair do |k,v|
|
117
|
+
begin
|
118
|
+
v.fetch config, verbose
|
119
|
+
rescue AuthError => e
|
120
|
+
Misc.die! "authentication failure: #{e}"
|
121
|
+
end
|
114
122
|
end
|
115
123
|
puts
|
124
|
+
SERVICES.each_pair do |k,v|
|
125
|
+
v.mirror config, verbose, noact
|
126
|
+
end
|
127
|
+
puts
|
128
|
+
if verbose
|
129
|
+
puts '=== Summary ==='; puts
|
130
|
+
SERVICES.each_pair do |k,v|
|
131
|
+
v.summarise config
|
132
|
+
end
|
133
|
+
puts
|
134
|
+
end
|
116
135
|
end # }}}1
|
117
136
|
|
118
|
-
# --
|
119
|
-
|
120
|
-
# run!
|
121
|
-
def self.main (verbose, noact, config)
|
122
|
-
auth, repos = process_config config
|
123
|
-
repositories = fetch verbose, auth, repos
|
124
|
-
mirror verbose, noact, repositories
|
125
|
-
summary repositories if verbose
|
126
|
-
end
|
127
|
-
|
128
137
|
end
|
129
138
|
|
130
139
|
# vim: set tw=70 sw=2 sts=2 et fdm=marker :
|
data/lib/gitbak/exec.rb
CHANGED
@@ -2,15 +2,17 @@
|
|
2
2
|
#
|
3
3
|
# File : gitbak/exec.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date :
|
5
|
+
# Date : 2014-02-19
|
6
6
|
#
|
7
|
-
# Copyright : Copyright (C)
|
8
|
-
# Licence :
|
7
|
+
# Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
# Licence : GPLv3+
|
9
9
|
#
|
10
10
|
# -- ; }}}1
|
11
11
|
|
12
12
|
require 'gitbak'
|
13
|
-
require 'gitbak/
|
13
|
+
require 'gitbak/misc'
|
14
|
+
require 'gitbak/services'
|
15
|
+
require 'gitbak/version'
|
14
16
|
|
15
17
|
require 'optparse'
|
16
18
|
|
@@ -31,63 +33,50 @@ module GitBak
|
|
31
33
|
# --
|
32
34
|
|
33
35
|
# parse command line options; die on failure
|
34
|
-
def self.parse_options
|
36
|
+
def self.parse_options(args) # {{{1
|
35
37
|
args_ = args.dup
|
36
38
|
options = { cfgfile: "#{Dir.home}/.gitbak", verbose: false,
|
37
39
|
noact: false }
|
38
|
-
|
39
|
-
op = OptionParser.new do |opts| # {{{2
|
40
|
+
op = OptionParser.new do |opts|
|
40
41
|
opts.banner = USAGE
|
41
|
-
|
42
42
|
opts.on('-c', '--config-file FILE',
|
43
43
|
'Configuration file') do |x|
|
44
44
|
options[:cfgfile] = x
|
45
45
|
end
|
46
|
-
|
47
46
|
opts.on('-v', '--[no-]verbose', 'Run verbosely') do |x|
|
48
47
|
options[:verbose] = x
|
49
48
|
end
|
50
|
-
|
51
49
|
opts.on('-n', '--no-act', 'List w/o mirroring') do |x|
|
52
50
|
options[:noact] = !x
|
53
51
|
end
|
54
|
-
|
55
52
|
opts.on_tail('-h', '--help', 'Show this message') do
|
56
|
-
puts INFO, '', opts
|
57
|
-
exit
|
53
|
+
puts INFO, '', opts; exit
|
58
54
|
end
|
59
|
-
|
60
55
|
opts.on_tail('--version', 'Show version') do
|
61
|
-
puts "gitbak v#{GitBak::VERSION}"
|
62
|
-
exit
|
56
|
+
puts "gitbak v#{GitBak::VERSION}"; exit
|
63
57
|
end
|
64
|
-
end
|
65
|
-
|
58
|
+
end
|
66
59
|
begin
|
67
60
|
op.parse! args_
|
68
61
|
rescue OptionParser::ParseError => e
|
69
62
|
GitBak::Misc.die! "#{e}\n\n#{op}"
|
70
63
|
end
|
71
|
-
|
72
64
|
GitBak::Misc.die! "usage: #{USAGE}" unless args_.length == 0
|
73
|
-
|
74
65
|
options
|
75
66
|
end # }}}1
|
76
67
|
|
77
68
|
# parse configuration file; die on failure
|
78
|
-
def self.
|
69
|
+
def self.configure(file)
|
79
70
|
GitBak::Misc.die! "configuration file (#{file}) not found" \
|
80
71
|
unless GitBak::Misc.exists? file
|
81
|
-
|
82
|
-
GitBak::Config.load file
|
72
|
+
GitBak.configure file
|
83
73
|
end
|
84
74
|
|
85
75
|
# run!
|
86
|
-
def self.main
|
87
|
-
options = parse_options
|
88
|
-
cfg =
|
89
|
-
|
90
|
-
GitBak.main options[:verbose], options[:noact], cfg.data
|
76
|
+
def self.main(args = nil)
|
77
|
+
options = parse_options(args || ARGV)
|
78
|
+
cfg = configure options[:cfgfile]
|
79
|
+
GitBak.main options[:verbose], options[:noact], cfg
|
91
80
|
end
|
92
81
|
|
93
82
|
end
|
data/lib/gitbak/misc.rb
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
#
|
3
3
|
# File : gitbak/misc.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date :
|
5
|
+
# Date : 2014-02-19
|
6
6
|
#
|
7
|
-
# Copyright : Copyright (C)
|
8
|
-
# Licence :
|
7
|
+
# Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
# Licence : GPLv3+
|
9
9
|
#
|
10
10
|
# -- ; }}}1
|
11
11
|
|
@@ -27,36 +27,22 @@ module GitBak
|
|
27
27
|
|
28
28
|
# --
|
29
29
|
|
30
|
-
# deep copy using Marshal
|
31
|
-
def self.deepdup (obj)
|
32
|
-
Marshal.load(Marshal.dump obj)
|
33
|
-
end
|
34
|
-
|
35
30
|
# print msg to stderr and exit
|
36
31
|
def self.die! (msg)
|
37
|
-
STDERR.puts msg
|
38
|
-
exit 1
|
32
|
+
STDERR.puts msg; exit 1
|
39
33
|
end
|
40
34
|
|
41
35
|
# does file/dir or symlink exists?
|
42
36
|
def self.exists? (path)
|
43
|
-
File.exists?(path)
|
37
|
+
File.exists?(path) || File.symlink?(path)
|
44
38
|
end
|
45
39
|
|
46
40
|
# prompt for line; optionally hide input
|
47
|
-
def self.prompt (prompt, hide = false)
|
48
|
-
STDOUT.print prompt
|
49
|
-
STDOUT.
|
50
|
-
|
51
|
-
|
52
|
-
line = STDIN.noecho { |i| i.gets }
|
53
|
-
STDOUT.puts
|
54
|
-
else
|
55
|
-
line = STDIN.gets
|
56
|
-
end
|
57
|
-
|
58
|
-
line and line.chomp
|
59
|
-
end # }}}1
|
41
|
+
def self.prompt (prompt, hide = false)
|
42
|
+
STDOUT.print prompt; STDOUT.flush
|
43
|
+
(line = hide ? STDIN.noecho { |i| i.gets } .tap { STDOUT.puts }
|
44
|
+
: STDIN.gets) && line.chomp
|
45
|
+
end
|
60
46
|
|
61
47
|
# execute command
|
62
48
|
# @raise SysError on failure
|
@@ -69,10 +55,8 @@ module GitBak
|
|
69
55
|
# @see sys_
|
70
56
|
def self.sys (cmd, *args) # {{{1
|
71
57
|
opts = Hash === args.last ? args.pop : {}
|
72
|
-
|
73
58
|
puts "$ #{ ([cmd] + args).join ' ' }" \
|
74
59
|
if opts[:verbose] or opts[:noact]
|
75
|
-
|
76
60
|
if opts[:noact]
|
77
61
|
puts '(not actually doing anything)'
|
78
62
|
else
|
data/lib/gitbak/services.rb
CHANGED
@@ -2,156 +2,15 @@
|
|
2
2
|
#
|
3
3
|
# File : gitbak/services.rb
|
4
4
|
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date :
|
5
|
+
# Date : 2014-02-19
|
6
6
|
#
|
7
|
-
# Copyright : Copyright (C)
|
8
|
-
# Licence :
|
7
|
+
# Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
# Licence : GPLv3+
|
9
9
|
#
|
10
10
|
# -- ; }}}1
|
11
11
|
|
12
|
-
require 'gitbak/
|
13
|
-
|
14
|
-
require '
|
15
|
-
require 'open-uri'
|
16
|
-
|
17
|
-
# --
|
18
|
-
|
19
|
-
# gitbak namespace
|
20
|
-
module GitBak
|
21
|
-
|
22
|
-
# git hosting services
|
23
|
-
module Services
|
24
|
-
|
25
|
-
# authentication error
|
26
|
-
class AuthError < GitBak::Error; end
|
27
|
-
|
28
|
-
# --
|
29
|
-
|
30
|
-
# avaliable services
|
31
|
-
SERVICES = %w{ bitbucket github gist }.map(&:to_sym)
|
32
|
-
|
33
|
-
# use another service's authentication
|
34
|
-
USE_AUTH = { gist: :github }
|
35
|
-
|
36
|
-
# API urls
|
37
|
-
APIS = {
|
38
|
-
bitbucket: ->(user) { "api.bitbucket.org/1.0/users/#{user}" },
|
39
|
-
github: ->(user) { "api.github.com/users/#{user}/repos" },
|
40
|
-
gist: ->(user) { "api.github.com/users/#{user}/gists" },
|
41
|
-
}
|
42
|
-
|
43
|
-
# github pagination
|
44
|
-
GH_PAGES = ->(data; l, n, r) {
|
45
|
-
(l = data.meta['link']) &&
|
46
|
-
(n = l.split(',').grep(/rel="next"/).first) &&
|
47
|
-
(r = l.match(%r{<(https://[^>]*)>})) && r[1]
|
48
|
-
}
|
49
|
-
|
50
|
-
# pagination
|
51
|
-
PAGES = { github: GH_PAGES, gist: GH_PAGES }
|
52
|
-
|
53
|
-
# JSON pages concat
|
54
|
-
JCAT = ->(pages) { pages.map { |x| JSON.load x } .flatten 1 }
|
55
|
-
|
56
|
-
# remote urls
|
57
|
-
REMOTES = # {{{1
|
58
|
-
{
|
59
|
-
bitbucket: {
|
60
|
-
ssh: ->(u, r) { "git@bitbucket.org:#{u}/#{r}.git" },
|
61
|
-
https: ->(u, r) { "https://bitbucket.org/#{u}/#{r}.git" },
|
62
|
-
},
|
63
|
-
github: {
|
64
|
-
ssh: ->(u, r) { "git@github.com:#{u}/#{r}.git" },
|
65
|
-
https: ->(u, r) { "https://github.com/#{u}/#{r}.git" },
|
66
|
-
},
|
67
|
-
gist: {
|
68
|
-
ssh: ->(id) { "git@gist.github.com:#{id}.git" },
|
69
|
-
https: ->(id) { "https://gist.github.com/#{id}.git" },
|
70
|
-
},
|
71
|
-
} # }}}1
|
72
|
-
|
73
|
-
# long keyword^wsymbol ;-)
|
74
|
-
AUTH = :http_basic_authentication
|
75
|
-
|
76
|
-
# --
|
77
|
-
|
78
|
-
# get data from API
|
79
|
-
# @raise AuthError on 401
|
80
|
-
def self.api_get_ (url, auth, service, user) # {{{1
|
81
|
-
opts = auth ? { AUTH => [auth[:user], auth[:pass]] } : {}
|
82
|
-
|
83
|
-
begin
|
84
|
-
data = open url, opts
|
85
|
-
rescue OpenURI::HTTPError => e
|
86
|
-
if e.io.status[0] == '401'
|
87
|
-
raise AuthError,
|
88
|
-
"401 for #{auth[:user]} on #{service}/#{user}"
|
89
|
-
else
|
90
|
-
raise
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
data
|
95
|
-
end # }}}1
|
96
|
-
|
97
|
-
# paginate
|
98
|
-
def self.paginate (pag, url, &b)
|
99
|
-
pages = []
|
100
|
-
while url; pages << data = b[url]; url = pag[data]; end
|
101
|
-
pages
|
102
|
-
end
|
103
|
-
|
104
|
-
# get data from API (w/ pagination if necessary); see api_get_
|
105
|
-
# @return [String]
|
106
|
-
# @return [<String>] if paginated
|
107
|
-
def self.api_get (service, user, auth)
|
108
|
-
get = ->(u) { api_get_ u, auth, service, user }
|
109
|
-
url = "https://#{APIS[service][user]}"
|
110
|
-
pag = PAGES[service]
|
111
|
-
pag ? paginate(pag, url) { |u| get[u] } : get[url]
|
112
|
-
end
|
113
|
-
|
114
|
-
# get repositories from service; uses api_get if service in APIS,
|
115
|
-
# api_get_<service> otherwise
|
116
|
-
#
|
117
|
-
# @return [<Hash>] [!{name:, remote:, description:},...]
|
118
|
-
def self.repositories (service, cfg, auth)
|
119
|
-
rem = REMOTES[service][cfg.fetch(:method, :ssh).to_sym]
|
120
|
-
args = [service, cfg[:user], auth]
|
121
|
-
data = APIS[service] ? api_get(*args) :
|
122
|
-
send("api_get_#{service}", *args)
|
123
|
-
send service, cfg, data, rem
|
124
|
-
end
|
125
|
-
|
126
|
-
# --
|
127
|
-
|
128
|
-
# turn bitbucket API data into a list of repositories
|
129
|
-
def self.bitbucket (cfg, data, rem)
|
130
|
-
data_ = JSON.load data
|
131
|
-
repos = data_['repositories'].select { |r| r['scm'] == 'git' }
|
132
|
-
repos.map do |r|
|
133
|
-
{ name: r['name'], remote: rem[cfg[:user], r['name']],
|
134
|
-
description: r['description'] }
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
# turn github API data into a list of repositories
|
139
|
-
def self.github (cfg, data, rem)
|
140
|
-
JCAT[data].map do |r|
|
141
|
-
{ name: r['name'], remote: rem[cfg[:user], r['name']],
|
142
|
-
description: r['description'] }
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
# turn gist API data into a list of repositories
|
147
|
-
def self.gist (cfg, data, rem)
|
148
|
-
JCAT[data].map do |r|
|
149
|
-
{ name: r['id'], remote: rem[r['id']],
|
150
|
-
description: r['description'] }
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
end
|
155
|
-
end
|
12
|
+
require 'gitbak/services/bitbucket'
|
13
|
+
require 'gitbak/services/gist'
|
14
|
+
require 'gitbak/services/github'
|
156
15
|
|
157
16
|
# vim: set tw=70 sw=2 sts=2 et fdm=marker :
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# -- ; {{{1
|
2
|
+
#
|
3
|
+
# File : gitbak/services/bitbucket.rb
|
4
|
+
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
+
# Date : 2014-02-19
|
6
|
+
#
|
7
|
+
# Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
# Licence : GPLv3+
|
9
|
+
#
|
10
|
+
# -- ; }}}1
|
11
|
+
|
12
|
+
require 'gitbak'
|
13
|
+
|
14
|
+
module GitBak::Services
|
15
|
+
|
16
|
+
module Bitbucket
|
17
|
+
|
18
|
+
API = -> user { "https://bitbucket.org/api/2.0/repositories/#{user}" }
|
19
|
+
SSH = -> user, repo { "git@bitbucket.org:#{user}/#{repo}.git" }
|
20
|
+
HTTPS = -> user, repo { "https://bitbucket.org/#{user}/#{repo}.git" }
|
21
|
+
|
22
|
+
def self.to_json(data)
|
23
|
+
JSON.load data
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.next_page(data)
|
27
|
+
data['next']
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.cat_json_values(pages)
|
31
|
+
pages.map { |x| x['values'] } .flatten(1) .sort_by { |x| x['name'] }
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.configure(cfg, dir, opts = {}) # {{{1
|
35
|
+
u = opts[:user] or raise 'no user' # TODO
|
36
|
+
c = cfg[:bitbucket] ||= []
|
37
|
+
a = if opts[:auth] == false
|
38
|
+
nil
|
39
|
+
else
|
40
|
+
p = GitBak::Misc.prompt "password for bitbucket/#{u}: ", true
|
41
|
+
{ user: u, pass: p }
|
42
|
+
end
|
43
|
+
c << { dir: dir, user: u, auth: a, method: opts[:method] }
|
44
|
+
end # }}}1
|
45
|
+
|
46
|
+
def self.fetch(cfg, verbose) # {{{1
|
47
|
+
f = method :to_json; pag = method :next_page
|
48
|
+
join = method :cat_json_values
|
49
|
+
(cfg[:bitbucket] || []).each do |x|
|
50
|
+
meth = x[:method] == :https ? HTTPS : SSH
|
51
|
+
info = "bitbucket/#{x[:user]}"
|
52
|
+
print "fetching #{info} ..." if verbose
|
53
|
+
repos = GitBak.paginate API[x[:user]], nil, info, x[:auth], f,
|
54
|
+
pag, join
|
55
|
+
x[:repos] = repos.select { |r| r['scm'] == 'git' } .map do |r|
|
56
|
+
{ name: r['name'], remote: meth[x[:user], r['name']],
|
57
|
+
description: r['description'] }
|
58
|
+
end
|
59
|
+
puts " #{repos.length} repositories" if verbose
|
60
|
+
end
|
61
|
+
end # }}}1
|
62
|
+
|
63
|
+
def self.mirror(cfg, verbose, noact) # {{{1
|
64
|
+
(cfg[:bitbucket] || []).each do |x|
|
65
|
+
x[:repos].each do |r|
|
66
|
+
GitBak.show_mirror 'bitbucket', x[:user], r[:name], r[:description]
|
67
|
+
GitBak.mirror_repo verbose, noact, r[:remote], x[:dir]
|
68
|
+
puts if verbose
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end # }}}1
|
72
|
+
|
73
|
+
def self.summarise(cfg)
|
74
|
+
(cfg[:bitbucket] || []).each do |x|
|
75
|
+
GitBak.show_summary "bitbucket/#{x[:user]}", x[:repos].length
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
GitBak::SERVICES[:bitbucket] = Bitbucket
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
# vim: set tw=70 sw=2 sts=2 et fdm=marker :
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# -- ; {{{1
|
2
|
+
#
|
3
|
+
# File : gitbak/services/gist.rb
|
4
|
+
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
+
# Date : 2014-02-19
|
6
|
+
#
|
7
|
+
# Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
# Licence : GPLv3+
|
9
|
+
#
|
10
|
+
# -- ; }}}1
|
11
|
+
|
12
|
+
require 'gitbak'
|
13
|
+
require 'gitbak/services/github'
|
14
|
+
|
15
|
+
module GitBak::Services
|
16
|
+
|
17
|
+
module Gist
|
18
|
+
|
19
|
+
API = 'https://api.github.com/gists'
|
20
|
+
SSH = -> id { "git@gist.github.com:#{id}.git" }
|
21
|
+
HTTPS = -> id { "https://gist.github.com/#{id}.git" }
|
22
|
+
|
23
|
+
HEADERS = GitHub::HEADERS
|
24
|
+
|
25
|
+
def self.configure(cfg, dir, opts = {}) # {{{1
|
26
|
+
u = opts[:user] or raise 'no user' # TODO
|
27
|
+
c = cfg[:gist] ||= []
|
28
|
+
a = if opts[:auth] == false
|
29
|
+
nil
|
30
|
+
elsif opts[:auth] == :github
|
31
|
+
(cfg[:github].find { |x| x[:user] == u })[:auth]
|
32
|
+
elsif opts[:token]
|
33
|
+
t = GitBak::Misc.prompt "token for gist/#{u}: ", true
|
34
|
+
{ user: t, pass: '' }
|
35
|
+
else
|
36
|
+
p = GitBak::Misc.prompt "password for gist/#{u}: ", true
|
37
|
+
{ user: u, pass: p }
|
38
|
+
end
|
39
|
+
c << { dir: dir, user: u, auth: a, method: opts[:method] }
|
40
|
+
end # }}}1
|
41
|
+
|
42
|
+
def self.fetch(cfg, verbose) # {{{1
|
43
|
+
pag = GitHub.method :next_page; join = GitBak.method :cat_json
|
44
|
+
(cfg[:gist] || []).each do |x|
|
45
|
+
meth = x[:method] == :https ? HTTPS : SSH
|
46
|
+
info = "gist/#{x[:user]}"
|
47
|
+
print "fetching #{info} ..." if verbose
|
48
|
+
repos = GitBak.paginate API, HEADERS, info, x[:auth], nil,
|
49
|
+
pag, join
|
50
|
+
x[:repos] = repos.map do |r|
|
51
|
+
{ name: r['id'], remote: meth[r['id']],
|
52
|
+
description: r['description'] }
|
53
|
+
end
|
54
|
+
puts " #{repos.length} repositories" if verbose
|
55
|
+
end
|
56
|
+
end # }}}1
|
57
|
+
|
58
|
+
def self.mirror(cfg, verbose, noact) # {{{1
|
59
|
+
(cfg[:gist] || []).each do |x|
|
60
|
+
x[:repos].each do |r|
|
61
|
+
GitBak.show_mirror 'gist', x[:user], r[:name], r[:description]
|
62
|
+
GitBak.mirror_repo verbose, noact, r[:remote], x[:dir]
|
63
|
+
puts if verbose
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end # }}}1
|
67
|
+
|
68
|
+
def self.summarise(cfg)
|
69
|
+
(cfg[:gist] || []).each do |x|
|
70
|
+
GitBak.show_summary "gist/#{x[:user]}", x[:repos].length
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
GitBak::SERVICES[:gist] = Gist
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
# vim: set tw=70 sw=2 sts=2 et fdm=marker :
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# -- ; {{{1
|
2
|
+
#
|
3
|
+
# File : gitbak/services/github.rb
|
4
|
+
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
+
# Date : 2014-02-19
|
6
|
+
#
|
7
|
+
# Copyright : Copyright (C) 2014 Felix C. Stegerman
|
8
|
+
# Licence : GPLv3+
|
9
|
+
#
|
10
|
+
# -- ; }}}1
|
11
|
+
|
12
|
+
require 'gitbak'
|
13
|
+
|
14
|
+
module GitBak::Services
|
15
|
+
|
16
|
+
module GitHub
|
17
|
+
|
18
|
+
API = 'https://api.github.com/user/repos?type=owner'
|
19
|
+
SSH = -> user, repo { "git@github.com:#{user}/#{repo}.git" }
|
20
|
+
HTTPS = -> user, repo { "https://github.com/#{user}/#{repo}.git" }
|
21
|
+
|
22
|
+
HEADERS = { 'Accept' => 'application/vnd.github.v3+json' }
|
23
|
+
|
24
|
+
def self.next_page(data)
|
25
|
+
(l = data.meta['link']) &&
|
26
|
+
(n = l.split(',').grep(/rel="next"/).first) &&
|
27
|
+
(r = l.match(%r{<(https://[^>]*)>})) && r[1]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.configure(cfg, dir, opts = {}) # {{{1
|
31
|
+
u = opts[:user] or raise 'no user' # TODO
|
32
|
+
c = cfg[:github] ||= []
|
33
|
+
a = if opts[:auth] == false
|
34
|
+
nil
|
35
|
+
elsif opts[:token]
|
36
|
+
t = GitBak::Misc.prompt "token for github/#{u}: ", true
|
37
|
+
{ user: t, pass: '' }
|
38
|
+
else
|
39
|
+
p = GitBak::Misc.prompt "password for github/#{u}: ", true
|
40
|
+
{ user: u, pass: p }
|
41
|
+
end
|
42
|
+
c << { dir: dir, user: u, auth: a, method: opts[:method] }
|
43
|
+
end # }}}1
|
44
|
+
|
45
|
+
def self.fetch(cfg, verbose) # {{{1
|
46
|
+
pag = method :next_page; join = GitBak.method :cat_json
|
47
|
+
(cfg[:github] || []).each do |x|
|
48
|
+
meth = x[:method] == :https ? HTTPS : SSH
|
49
|
+
info = "github/#{x[:user]}"
|
50
|
+
print "fetching #{info} ..." if verbose
|
51
|
+
repos = GitBak.paginate API, HEADERS, info, x[:auth], nil,
|
52
|
+
pag, join
|
53
|
+
x[:repos] = repos.map do |r|
|
54
|
+
{ name: r['name'], remote: meth[x[:user], r['name']],
|
55
|
+
description: r['description'] }
|
56
|
+
end
|
57
|
+
puts " #{repos.length} repositories" if verbose
|
58
|
+
end
|
59
|
+
end # }}}1
|
60
|
+
|
61
|
+
def self.mirror(cfg, verbose, noact) # {{{1
|
62
|
+
(cfg[:github] || []).each do |x|
|
63
|
+
x[:repos].each do |r|
|
64
|
+
GitBak.show_mirror 'github', x[:user], r[:name], r[:description]
|
65
|
+
GitBak.mirror_repo verbose, noact, r[:remote], x[:dir]
|
66
|
+
puts if verbose
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end # }}}1
|
70
|
+
|
71
|
+
def self.summarise(cfg)
|
72
|
+
(cfg[:github] || []).each do |x|
|
73
|
+
GitBak.show_summary "github/#{x[:user]}", x[:repos].length
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
GitBak::SERVICES[:github] = GitHub
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
# vim: set tw=70 sw=2 sts=2 et fdm=marker :
|
data/lib/gitbak/version.rb
CHANGED
metadata
CHANGED
@@ -1,44 +1,25 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gitbak
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.5.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Felix C. Stegerman
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
|
16
|
-
requirement: &17607720 !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *17607720
|
25
|
-
description: ! 'GitBak mirrors Bitbucket/GitHub/Gist repositories; paths, users,
|
26
|
-
|
11
|
+
date: 2014-02-19 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
GitBak mirrors Bitbucket/GitHub/Gist repositories; paths, users,
|
27
15
|
and authentication are specified in ~/.gitbak.
|
28
16
|
|
29
|
-
|
30
17
|
When run, gitbak:
|
31
18
|
|
32
|
-
|
33
|
-
* asks for unspecified passwords;
|
34
|
-
|
19
|
+
* asks for passwords;
|
35
20
|
* lists repositories using APIs - authenticating if necessary;
|
36
|
-
|
37
21
|
* clones/updates repositories;
|
38
|
-
|
39
22
|
* shows a summary (if verbose)
|
40
|
-
|
41
|
-
'
|
42
23
|
email:
|
43
24
|
- flx@obfusk.net
|
44
25
|
executables:
|
@@ -46,41 +27,40 @@ executables:
|
|
46
27
|
extensions: []
|
47
28
|
extra_rdoc_files: []
|
48
29
|
files:
|
49
|
-
- .yardopts
|
30
|
+
- ".yardopts"
|
50
31
|
- README.md
|
51
|
-
- bin/gitbak
|
52
32
|
- gitbak.gemspec
|
53
|
-
- lib/gitbak.rb
|
54
|
-
- lib/gitbak/version.rb
|
55
33
|
- lib/gitbak/misc.rb
|
56
|
-
- lib/gitbak/
|
57
|
-
- lib/gitbak/
|
34
|
+
- lib/gitbak/services/bitbucket.rb
|
35
|
+
- lib/gitbak/services/gist.rb
|
36
|
+
- lib/gitbak/services/github.rb
|
58
37
|
- lib/gitbak/services.rb
|
38
|
+
- lib/gitbak/version.rb
|
59
39
|
- lib/gitbak/exec.rb
|
40
|
+
- lib/gitbak.rb
|
41
|
+
- bin/gitbak
|
60
42
|
homepage: https://github.com/obfusk/gitbak
|
61
43
|
licenses:
|
62
|
-
-
|
44
|
+
- GPLv3+
|
45
|
+
metadata: {}
|
63
46
|
post_install_message:
|
64
47
|
rdoc_options: []
|
65
48
|
require_paths:
|
66
49
|
- lib
|
67
50
|
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
-
none: false
|
69
51
|
requirements:
|
70
|
-
- -
|
52
|
+
- - ">="
|
71
53
|
- !ruby/object:Gem::Version
|
72
54
|
version: 1.9.1
|
73
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
-
none: false
|
75
56
|
requirements:
|
76
|
-
- -
|
57
|
+
- - ">="
|
77
58
|
- !ruby/object:Gem::Version
|
78
59
|
version: '0'
|
79
60
|
requirements: []
|
80
61
|
rubyforge_project:
|
81
|
-
rubygems_version:
|
62
|
+
rubygems_version: 2.0.14
|
82
63
|
signing_key:
|
83
|
-
specification_version:
|
64
|
+
specification_version: 4
|
84
65
|
summary: bitbucket/github/gist backup
|
85
66
|
test_files: []
|
86
|
-
has_rdoc:
|
data/lib/gitbak/config.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# -- ; {{{1
|
2
|
-
#
|
3
|
-
# File : gitbak/config.rb
|
4
|
-
# Maintainer : Felix C. Stegerman <flx@obfusk.net>
|
5
|
-
# Date : 2013-01-03
|
6
|
-
#
|
7
|
-
# Copyright : Copyright (C) 2013 Felix C. Stegerman
|
8
|
-
# Licence : GPLv2
|
9
|
-
#
|
10
|
-
# -- ; }}}1
|
11
|
-
|
12
|
-
require 'gitbak/eval'
|
13
|
-
require 'gitbak/misc'
|
14
|
-
require 'gitbak/services'
|
15
|
-
|
16
|
-
# --
|
17
|
-
|
18
|
-
# gitbak namespace
|
19
|
-
module GitBak
|
20
|
-
|
21
|
-
# configuration
|
22
|
-
module Config # {{{1
|
23
|
-
|
24
|
-
# configuration error
|
25
|
-
class ConfigError < GitBak::Error; end
|
26
|
-
|
27
|
-
# --
|
28
|
-
|
29
|
-
# configuration base class
|
30
|
-
class ServiceCfg # {{{2
|
31
|
-
# data
|
32
|
-
attr_reader :_data
|
33
|
-
|
34
|
-
# init
|
35
|
-
def initialize
|
36
|
-
@_data = {}
|
37
|
-
end
|
38
|
-
|
39
|
-
# pass on to _service or super
|
40
|
-
def method_missing (meth, *args, &block)
|
41
|
-
if GitBak::Services::SERVICES.include? meth
|
42
|
-
_service meth, *args
|
43
|
-
else
|
44
|
-
super
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end # }}}2
|
48
|
-
|
49
|
-
# authentication configuration
|
50
|
-
class AuthCfg < ServiceCfg # {{{2
|
51
|
-
# set service auth
|
52
|
-
def _service (name, user, pass = nil)
|
53
|
-
(@_data[name] ||= {})[user] = { user: user, pass: pass }
|
54
|
-
end
|
55
|
-
end # }}}2
|
56
|
-
|
57
|
-
# repository configuration
|
58
|
-
class ReposCfg < ServiceCfg # {{{2
|
59
|
-
# set service repo
|
60
|
-
def _service (name, dir, user, opts = {})
|
61
|
-
c = opts.merge dir: dir, user: user
|
62
|
-
c[:auth] = c[:user] if c[:auth] == true
|
63
|
-
(@_data[name] ||= []) << c
|
64
|
-
end
|
65
|
-
end # }}}2
|
66
|
-
|
67
|
-
# authentication and repository configuration
|
68
|
-
class Cfg # {{{2
|
69
|
-
# data
|
70
|
-
attr_reader :auth, :repos
|
71
|
-
|
72
|
-
# init
|
73
|
-
def initialize
|
74
|
-
@auth = AuthCfg.new
|
75
|
-
@repos = ReposCfg.new
|
76
|
-
end
|
77
|
-
|
78
|
-
# get data
|
79
|
-
def data # {{{3
|
80
|
-
auth = @auth._data
|
81
|
-
repos = @repos._data
|
82
|
-
|
83
|
-
{ auth: auth, repos: repos }
|
84
|
-
end # }}}3
|
85
|
-
end # }}}2
|
86
|
-
|
87
|
-
# --
|
88
|
-
|
89
|
-
# load configuration file
|
90
|
-
# @raise ConfigError unless file returns Cfg
|
91
|
-
def self.load (file) # {{{2
|
92
|
-
cfg = eval File.read(file), GitBak::Eval.new.binding, file # ???
|
93
|
-
|
94
|
-
raise ConfigError, "[#{file}] isn't a GitBak::Config::Cfg " \
|
95
|
-
"(#{cfg.class} instead)." \
|
96
|
-
unless Cfg === cfg
|
97
|
-
|
98
|
-
cfg
|
99
|
-
end # }}}2
|
100
|
-
|
101
|
-
end # }}}1
|
102
|
-
|
103
|
-
# configure
|
104
|
-
def self.configure (&block)
|
105
|
-
cfg = Config::Cfg.new
|
106
|
-
block[cfg.auth, cfg.repos]
|
107
|
-
cfg
|
108
|
-
end
|
109
|
-
|
110
|
-
end
|
111
|
-
|
112
|
-
# vim: set tw=70 sw=2 sts=2 et fdm=marker :
|