rbk 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e6e367fdbafadf979ed97c0d452e829d7b7619e8
4
+ data.tar.gz: a4b498e6a87a233e831025d63c1a0ccab1ea7424
5
+ SHA512:
6
+ metadata.gz: cfb23a69663a06035aa44b7f93e884248fcd4d306973c6d672f8f5f90697f5e390363a76ae5b177d2ff367f9133599dbd494156b9e8d5e0d4fbed2fbb20cb49f
7
+ data.tar.gz: 75aac9e2c3ad21bbebc922693fc5b5af70365f85ec1d64a3e354aaaaa873fe68fc59b382e5a0215db0074249210d8abe5f34f2bdfbdcbff411fd21c31c0f2485
@@ -0,0 +1,12 @@
1
+ Copyright (c) 2015, Mathias Söderberg
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+
8
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+
10
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,51 @@
1
+ # rbk
2
+
3
+ [![Build Status](https://travis-ci.org/mthssdrbrg/rbk.svg?branch=master)](https://travis-ci.org/mthssdrbrg/rbk)
4
+ [![Coverage Status](https://coveralls.io/repos/mthssdrbrg/rbk/badge.svg?branch=master)](https://coveralls.io/r/mthssdrbrg/rbk?branch=master)
5
+ [![Gem Version](https://badge.fury.io/rb/rbk.svg)](http://badge.fury.io/rb/rbk)
6
+
7
+ rbk, short for "Repo BacKup", is a small tool written in Ruby that attempts to
8
+ make it easy to backup all repos belonging to an organization's GitHub
9
+ account to a specific bucket on S3.
10
+
11
+ ## Installation
12
+
13
+ ```
14
+ [sudo] gem install rbk
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ The following example shows the simplest invocation of `rbk`:
20
+
21
+ ```
22
+ rbk -o <organization-name> -b <s3-bucket>
23
+ ```
24
+
25
+ This does however assume that the following environment variables are available:
26
+
27
+ - `GITHUB_ACCESS_TOKEN`
28
+ - `AWS_ACCESS_KEY_ID`
29
+ - `AWS_SECRET_ACCESS_KEY`
30
+
31
+ It is also possible to specify them as arguments to `rbk`:
32
+
33
+ ```
34
+ rbk -o <organization-name> -b <s3-bucket> --github-access-token=<TOKEN> --access-key-id=<KEY> --secret-access-key=<KEY>
35
+ ```
36
+
37
+ See `rbk -h` for further usage:
38
+
39
+ ```
40
+ Usage: rbk [options]
41
+ -o, --organization=NAME (GitHub) Organization name
42
+ -b, --bucket=NAME S3 bucket where to store backups
43
+ --github-access-token=TOKEN GitHub access token
44
+ --access-key-id=KEY AWS access key id
45
+ --secret-access-key=KEY AWS secret access key
46
+ -h, --help Display this screen
47
+ ```
48
+
49
+ ## Copyright
50
+
51
+ © 2015 Mathias Söderberg, see LICENSE.txt (BSD 3-Clause).
data/bin/rbk ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $: << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'rbk'
6
+
7
+ exit(Rbk::Cli.run(ARGV))
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ require 'aws-sdk-v1'
4
+ require 'git'
5
+ require 'github_api'
6
+ require 'fileutils'
7
+
8
+
9
+ module Rbk
10
+ RbkError = Class.new(StandardError)
11
+ ExecError = Class.new(RbkError)
12
+ InsufficientOptionsError = Class.new(RbkError)
13
+ end
14
+
15
+ require 'rbk/backup'
16
+ require 'rbk/cli'
17
+ require 'rbk/configuration'
18
+ require 'rbk/shell'
19
+ require 'rbk/uploader'
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ module Rbk
4
+ class Backup
5
+ def initialize(repos, git, archiver, uploader, shell, fileutils=FileUtils)
6
+ @repos = repos
7
+ @git = git
8
+ @archiver = archiver
9
+ @uploader = uploader
10
+ @shell = shell
11
+ @fileutils = fileutils
12
+ @date_suffix = Date.today.strftime('%Y%m%d')
13
+ end
14
+
15
+ def run
16
+ @repos.each do |repo|
17
+ clone_path = %(#{repo.name}-#{@date_suffix}.git)
18
+ @shell.puts(%(Cloning "#{repo.name}" to "#{clone_path}"))
19
+ if cloned?(repo.ssh_url, clone_path)
20
+ archive = @archiver.create(clone_path)
21
+ @uploader.upload(archive)
22
+ @fileutils.remove_entry_secure(archive)
23
+ @fileutils.remove_entry_secure(clone_path)
24
+ else
25
+ @shell.puts(%(Failed to clone "#{repo.name}"))
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def cloned?(url, path)
33
+ @git.clone(url, path, bare: true)
34
+ true
35
+ rescue Git::GitExecuteError
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+
3
+ module Rbk
4
+ class Cli
5
+ def self.run(argv=ARGV, options={})
6
+ new(argv, options).run
7
+ end
8
+
9
+ def initialize(argv, options={})
10
+ @argv = argv
11
+ @options = options
12
+ @git = @options[:git] || Git
13
+ @github_repos = @options[:github_repos] || Github::Repos
14
+ @stderr = @options[:stderr] || $stderr
15
+ end
16
+
17
+ def run
18
+ @config = Configuration.create(@argv)
19
+ @config.validate
20
+ @shell = @options[:shell] || Shell.new(@config.quiet?)
21
+ @archiver = Archiver.new(@shell)
22
+ @s3 = @options[:s3] || AWS::S3.new(@config.aws_credentials)
23
+ @uploader = Uploader.new(@s3.buckets[@config.bucket], @shell)
24
+ if @config.help?
25
+ @shell.puts(@config.usage)
26
+ else
27
+ Backup.new(repos, git, archiver, uploader, shell).run
28
+ end
29
+ 0
30
+ rescue => e
31
+ @stderr.puts(%(#{e.message} (#{e.class})))
32
+ if e.is_a?(InsufficientOptionsError)
33
+ @stderr.puts(@config.usage)
34
+ end
35
+ 1
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :git, :archiver, :uploader, :shell
41
+
42
+ def repos
43
+ @repos ||= begin
44
+ r = @github_repos.new(oauth_token: @config.github_access_token)
45
+ r.list(org: @config.organization, auto_pagination: true)
46
+ end
47
+ end
48
+
49
+ class Archiver
50
+ def initialize(shell=Shell.new)
51
+ @shell = shell
52
+ end
53
+
54
+ def create(path)
55
+ archive = %(#{path}.tar.gz)
56
+ @shell.exec(%(tar czf #{archive} #{path}))
57
+ archive
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,119 @@
1
+ # encoding: utf-8
2
+
3
+ require 'optparse'
4
+
5
+
6
+ module Rbk
7
+ class Configuration
8
+ def self.create(argv)
9
+ self.load.parse(argv)
10
+ end
11
+
12
+ def self.load
13
+ if File.exists?('.rbk.yml')
14
+ config = YAML.load_file('.rbk.yml')
15
+ elsif File.exists?(File.join(Dir.home, '.rbk.yml'))
16
+ config = YAML.load_file(File.join(Dir.home, '.rbk.yml'))
17
+ else
18
+ config = {}
19
+ end
20
+ new(config)
21
+ end
22
+
23
+ def initialize(config={})
24
+ @config = defaults.merge(config)
25
+ @parser = create_parser
26
+ end
27
+
28
+ def parse(argv)
29
+ @parser.parse(argv)
30
+ self
31
+ end
32
+
33
+ def validate
34
+ invalid_option?(@config['bucket'], 'Missing S3 bucket')
35
+ invalid_option?(@config['github_access_token'], 'Missing GitHub access token')
36
+ invalid_option?(@config['organization'], 'Missing organization')
37
+ self
38
+ end
39
+
40
+ def method_missing(m, *args, &block)
41
+ if @config.key?(m.to_s)
42
+ @config[m.to_s]
43
+ else
44
+ super
45
+ end
46
+ end
47
+
48
+ def help?
49
+ !!@config['show_help']
50
+ end
51
+
52
+ def quiet?
53
+ !!@config['quiet']
54
+ end
55
+
56
+ def usage
57
+ @parser.to_s
58
+ end
59
+
60
+ def aws_credentials
61
+ {
62
+ access_key_id: @config['aws_access_key_id'],
63
+ secret_access_key: @config['aws_secret_access_key']
64
+ }
65
+ end
66
+
67
+ private
68
+
69
+ def invalid_option?(value, message)
70
+ if !help? && (!value || value.empty?)
71
+ raise InsufficientOptionsError, message
72
+ end
73
+ end
74
+
75
+ def defaults
76
+ {
77
+ 'aws_access_key_id' => nil,
78
+ 'aws_secret_access_key' => nil,
79
+ 'bucket' => nil,
80
+ 'github_access_token' => ENV['GITHUB_ACCESS_TOKEN'],
81
+ 'organization' => nil,
82
+ 'quiet' => false,
83
+ 'show_help' => false,
84
+ }
85
+ end
86
+
87
+ def create_parser
88
+ OptionParser.new do |opt|
89
+ opt.on('-o', '--organization=NAME', '(GitHub) Organization name') do |o|
90
+ @config['organization'] = o
91
+ end
92
+
93
+ opt.on('-b', '--bucket=NAME', 'S3 bucket where to store backups') do |b|
94
+ @config['bucket'] = b
95
+ end
96
+
97
+ opt.on('-G TOKEN', '--github-access-token=TOKEN', 'GitHub access token') do |token|
98
+ @config['github_access_token'] = token
99
+ end
100
+
101
+ opt.on('-A KEY', '--access-key-id=KEY', 'AWS access key id') do |key|
102
+ @config['aws_access_key_id'] = key
103
+ end
104
+
105
+ opt.on('-S KEY', '--secret-access-key=KEY', 'AWS secret access key') do |key|
106
+ @config['aws_secret_access_key'] = key
107
+ end
108
+
109
+ opt.on('-q', '--quiet', 'Be quiet and mind your own business') do |key|
110
+ @config['quiet'] = key
111
+ end
112
+
113
+ opt.on('-h', '--help', 'Display this screen') do
114
+ @config['show_help'] = true
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+
3
+ module Rbk
4
+ class Shell
5
+ def initialize(quiet=false, stream=$stdout)
6
+ @quiet = quiet
7
+ @stream = stream
8
+ end
9
+
10
+ def puts(message)
11
+ @stream.puts(message) unless @quiet
12
+ end
13
+
14
+ def exec(command)
15
+ output = %x(#{command})
16
+ unless $?.success?
17
+ raise ExecError, output
18
+ end
19
+ output
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ module Rbk
4
+ class Uploader
5
+ def initialize(bucket, shell, date=Date.today)
6
+ @bucket = bucket
7
+ @shell = shell
8
+ @date_prefix = (date || Date.today).strftime('%Y%m%d')
9
+ end
10
+
11
+ def upload(path)
12
+ s3_object = @bucket.objects[[@date_prefix, path].join('/')]
13
+ if s3_object.exists?
14
+ @shell.puts(%(s3://#{@bucket.name}/#{s3_object.key} already exists, skipping...))
15
+ else
16
+ @shell.puts(%(Writing #{path} to s3://#{@bucket.name}/#{s3_object.key}))
17
+ s3_object.write(Pathname.new(path))
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module Rbk
4
+ VERSION = '0.1.1'.freeze
5
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rbk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Mathias Söderberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk-v1
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.64'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.64'
27
+ - !ruby/object:Gem::Dependency
28
+ name: github_api
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.11'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '0.12'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '0.11'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '0.12'
47
+ - !ruby/object:Gem::Dependency
48
+ name: git
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.2'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.2'
61
+ description: Clones and uploads an organization's GitHub repositories to S3
62
+ email:
63
+ - mths@sdrbrg.se
64
+ executables:
65
+ - rbk
66
+ extensions: []
67
+ extra_rdoc_files: []
68
+ files:
69
+ - LICENSE.txt
70
+ - README.md
71
+ - bin/rbk
72
+ - lib/rbk.rb
73
+ - lib/rbk/backup.rb
74
+ - lib/rbk/cli.rb
75
+ - lib/rbk/configuration.rb
76
+ - lib/rbk/shell.rb
77
+ - lib/rbk/uploader.rb
78
+ - lib/rbk/version.rb
79
+ homepage: https://github.com/mthssdrbrg/rbk
80
+ licenses:
81
+ - BSD-3-Clause
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: 1.9.3
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.4.7
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: GitHub repo backup utility
103
+ test_files: []