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.
@@ -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
+ []: {{{1
2
2
 
3
3
  File : README
4
4
  Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
- Date : 2013-04-21
5
+ Date : 2014-02-19
6
6
 
7
- Copyright : Copyright (C) 2013 Felix C. Stegerman
8
- Version : v0.4.3
7
+ Copyright : Copyright (C) 2014 Felix C. Stegerman
8
+ Version : v0.5.0
9
9
 
10
- <!-- }}}1 -->
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 unspecified passwords;
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
- <!-- }}}1 -->
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
- $ gem install gitbak # rubygems
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
- GitBak.configure do |auth, services|
61
- %w{ bob alice }.each do |u|
62
- services.bitbucket "#{dir}/#{u}/bitbucket", u, auth: true
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
- auth.<service> user[, password]
72
- services.<service> dir, user[, options]
73
-
74
- The (default) services are: bitbucket, github, gist. GitBak will
75
- prompt for unspecified passwords.
76
-
77
- #### Repository Options
60
+ ```ruby
61
+ bitbucket repo #, options...
62
+ github repo #, options...
63
+ gist repo #, options...
64
+ ```
78
65
 
79
- :auth can be true (same user) or 'username'.
80
- :method defaults to :ssh.
66
+ ### Options
81
67
 
82
- <!-- }}}1 -->
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
- GPLv2 [1].
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
- <!-- }}}1 -->
105
+ [1] GNU General Public License, version 3
106
+ --- http://www.gnu.org/licenses/gpl-3.0.html
123
107
 
124
- <!-- vim: set tw=70 sw=2 sts=2 et fdm=marker : -->
108
+ []: ! ( vim: set tw=70 sw=2 sts=2 et fdm=marker : )
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
 
12
12
  When run, gitbak:
13
13
 
14
- * asks for unspecified passwords;
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 = 'GPLv2'
26
+ s.license = 'GPLv3+'
27
27
 
28
28
  s.executables = %w{ gitbak }
29
- s.files = %w{ .yardopts README.md bin/gitbak gitbak.gemspec }\
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
@@ -2,41 +2,47 @@
2
2
  #
3
3
  # File : gitbak.rb
4
4
  # Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
- # Date : 2013-01-03
5
+ # Date : 2014-02-19
6
6
  #
7
- # Copyright : Copyright (C) 2013 Felix C. Stegerman
8
- # Licence : GPLv2
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 (remote)
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); optionally
38
+ # clone (from remote) or update repository (in dir), optionally
30
39
  # verbose
31
- def self.mirror_repo (verbose, noact, remote, dir) # {{{1
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
- # check auth; ask passwords
56
- def self.process_config (config) # {{{1
57
- config_ = Misc.deepdup config
58
-
59
- config_[:repos].each do |service, cfgs|
60
- auth = config_[:auth][service] ||= {}
61
- cfgs.each do |cfg|
62
- user = cfg[:auth]
63
- auth[user] = { user: user, pass: nil } if user && !auth[user]
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
- config_[:auth].each do |service, auth|
68
- next if GitBak::Services::USE_AUTH[service]
69
- auth.each_value do |x|
70
- p = "#{service} password for #{x[:user]}: "
71
- x[:pass] ||= Misc.prompt p, true # TODO
72
- end
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
- [config_[:auth], config_[:repos]]
76
- end # }}}1
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
- # fetch repository lists; optionally verbose
79
- def self.fetch (verbose, auth, repos) # {{{1
80
- repos.map do |service, cfgs|
81
- au = auth[Services::USE_AUTH.fetch service, service]
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
- begin
86
- rs = Services.repositories service, cfg, au[cfg[:auth]]
87
- rescue Services::AuthError => e
88
- Misc.die! "authentication failure: #{e}"
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
- [service, cfg[:user], cfg[:dir], rs]
92
- end
93
- end .flatten 1
94
- end # }}}1
102
+ # --
95
103
 
96
- # mirror repositories; optionally verbose
97
- def self.mirror (verbose, noact, repos) # {{{1
98
- repos.each do |s, usr, dir, rs|
99
- rs.each do |r|
100
- name, desc = r[:name], r[:description]
101
- puts "==> #{s} | #{usr} | #{name} | #{desc} <==" if verbose
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
- # print summary
109
- def self.summary (repos) # {{{1
110
- puts '', '=== Summary ===', ''
111
- repos.each do |service, usr, dir, rs|
112
- printf " %-15s for %-20s: %10s repositories\n",
113
- service, usr, rs.length
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 :
@@ -2,15 +2,17 @@
2
2
  #
3
3
  # File : gitbak/exec.rb
4
4
  # Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
- # Date : 2013-01-03
5
+ # Date : 2014-02-19
6
6
  #
7
- # Copyright : Copyright (C) 2013 Felix C. Stegerman
8
- # Licence : GPLv2
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/config'
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 (args) # {{{1
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 # }}}2
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.parse_cfgfile (file)
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 (args = nil)
87
- options = parse_options (args or ARGV)
88
- cfg = parse_cfgfile options[:cfgfile]
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
@@ -2,10 +2,10 @@
2
2
  #
3
3
  # File : gitbak/misc.rb
4
4
  # Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
- # Date : 2013-01-03
5
+ # Date : 2014-02-19
6
6
  #
7
- # Copyright : Copyright (C) 2013 Felix C. Stegerman
8
- # Licence : GPLv2
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) or File.symlink?(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) # {{{1
48
- STDOUT.print prompt
49
- STDOUT.flush
50
-
51
- if hide
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
@@ -2,156 +2,15 @@
2
2
  #
3
3
  # File : gitbak/services.rb
4
4
  # Maintainer : Felix C. Stegerman <flx@obfusk.net>
5
- # Date : 2013-04-21
5
+ # Date : 2014-02-19
6
6
  #
7
- # Copyright : Copyright (C) 2013 Felix C. Stegerman
8
- # Licence : GPLv2
7
+ # Copyright : Copyright (C) 2014 Felix C. Stegerman
8
+ # Licence : GPLv3+
9
9
  #
10
10
  # -- ; }}}1
11
11
 
12
- require 'gitbak/misc'
13
-
14
- require 'json'
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 :
@@ -1,7 +1,7 @@
1
1
  module GitBak
2
2
  # version and ...
3
- VERSION = '0.4.3'
3
+ VERSION = '0.5.0'
4
4
 
5
5
  # ... date (for gemspec) ;-)
6
- DATE = '2013-04-21'
6
+ DATE = '2014-02-19'
7
7
  end
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.4.3
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: 2013-04-21 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: json
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/eval.rb
57
- - lib/gitbak/config.rb
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
- - GPLv2
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: 1.8.11
62
+ rubygems_version: 2.0.14
82
63
  signing_key:
83
- specification_version: 3
64
+ specification_version: 4
84
65
  summary: bitbucket/github/gist backup
85
66
  test_files: []
86
- has_rdoc:
@@ -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 :
@@ -1,6 +0,0 @@
1
- module GitBak
2
- # evaluation wrapper
3
- class Eval
4
- public :binding
5
- end
6
- end