davidsmalley-gem-thief 0.0.1
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.
- data/History.txt +3 -0
- data/Manifest.txt +12 -0
- data/README.txt +67 -0
- data/Rakefile +26 -0
- data/bin/thief +51 -0
- data/lib/gem_thief/export.rb +92 -0
- data/lib/gem_thief/import.rb +31 -0
- data/lib/gem_thief.rb +50 -0
- data/lib/thief.rb +27 -0
- data/spec/gem_thief_spec.rb +7 -0
- data/spec/spec_helper.rb +16 -0
- data/test/test_gem-thief.rb +0 -0
- metadata +65 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
gem-thief
|
2
|
+
by David Smalley
|
3
|
+
http://github.com/davidsmalley/gem-thief
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Gem Thief was designed to help me sync the various gem versions on the application servers running Litmus.
|
8
|
+
Everytime we deploy a new server I need to reinstall all the necessary gems and get the right versions. Rails
|
9
|
+
gem dependencies are great but they don't work across all of the apps we run including a few Ruby utility apps.
|
10
|
+
Gem thief is designed to be run on one machine which has the gems, and given the credentials to connect to the
|
11
|
+
remote machine which needs gems installing.
|
12
|
+
|
13
|
+
== FEATURES/PROBLEMS:
|
14
|
+
|
15
|
+
* Will mirror gems on your local computer to a remote computer using a number of different strategies
|
16
|
+
* Parses remote gems from the gem list --local command, not really ideal but works for now
|
17
|
+
(new RubyGem versions might break the expected output)
|
18
|
+
|
19
|
+
== SYNOPSIS:
|
20
|
+
|
21
|
+
./thief -h a.remote.machine.com -u root --strategy latest -x
|
22
|
+
|
23
|
+
You can also pass in the following flags:
|
24
|
+
--no-docs :Pass --no-ri --no-rdoc onto the gem install command
|
25
|
+
--no-deps :Do all installs with the --ignore-dependencies flag
|
26
|
+
--pretend :Just print a list of gems which would have been updated
|
27
|
+
|
28
|
+
Strategy has 4 options:
|
29
|
+
- all: Totally install all gems and versionsfrom the local machine regardless of what gems are already on the remote machine
|
30
|
+
- latest: Just install the most recent versions of all the gems on the local machine
|
31
|
+
- missing: Install all versions of gems which are on the local machine but not on the remote machine
|
32
|
+
- latest_missing: Install the most recent versions of any missing gems on the remote machine
|
33
|
+
|
34
|
+
== REQUIREMENTS:
|
35
|
+
|
36
|
+
* Net/ssh > 2
|
37
|
+
* RubyGems > 1.2
|
38
|
+
|
39
|
+
== INSTALL:
|
40
|
+
|
41
|
+
* Install the gem
|
42
|
+
* run Thief command from machine which has the desired gems
|
43
|
+
|
44
|
+
== LICENSE:
|
45
|
+
|
46
|
+
(The MIT License)
|
47
|
+
|
48
|
+
Copyright (c) 2008
|
49
|
+
|
50
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
51
|
+
a copy of this software and associated documentation files (the
|
52
|
+
'Software'), to deal in the Software without restriction, including
|
53
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
54
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
55
|
+
permit persons to whom the Software is furnished to do so, subject to
|
56
|
+
the following conditions:
|
57
|
+
|
58
|
+
The above copyright notice and this permission notice shall be
|
59
|
+
included in all copies or substantial portions of the Software.
|
60
|
+
|
61
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
62
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
63
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
64
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
65
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
66
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
67
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bones'
|
7
|
+
Bones.setup
|
8
|
+
rescue LoadError
|
9
|
+
load 'tasks/setup.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
ensure_in_path 'lib'
|
13
|
+
require 'gem_thief'
|
14
|
+
|
15
|
+
task :default => 'spec:run'
|
16
|
+
|
17
|
+
PROJ.name = 'gem-thief'
|
18
|
+
PROJ.authors = 'David Smalley'
|
19
|
+
PROJ.email = 'david[at]davidsmalley.com'
|
20
|
+
PROJ.url = 'http://github.com/davidsmalley/gem-thief'
|
21
|
+
PROJ.version = GemThief::VERSION
|
22
|
+
PROJ.rubyforge.name = 'gem-thief'
|
23
|
+
|
24
|
+
PROJ.spec.opts << '--color'
|
25
|
+
|
26
|
+
# EOF
|
data/bin/thief
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path(
|
4
|
+
File.join(File.dirname(__FILE__), %w[.. lib gem_thief]))
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'pp'
|
8
|
+
|
9
|
+
options = {}
|
10
|
+
OptionParser.new do |opts|
|
11
|
+
|
12
|
+
opts.banner
|
13
|
+
|
14
|
+
# no argument, shows at tail
|
15
|
+
opts.on_tail("--help", "show this message") {puts opts; exit}
|
16
|
+
|
17
|
+
# mandatory argument
|
18
|
+
opts.on("-h", "--remote-host [HOST]",
|
19
|
+
"Remote host that",
|
20
|
+
"gems will be mirrored to") do |o|
|
21
|
+
options[:host] = o
|
22
|
+
end
|
23
|
+
opts.on("-u", "--user [USER]",
|
24
|
+
"The user to connect as") do |o|
|
25
|
+
options[:user] = o
|
26
|
+
end
|
27
|
+
opts.on("-p", "--password [PASSWORD]",
|
28
|
+
"The password to connect", "Not required if you have a public key setup already") do |o|
|
29
|
+
options[:password] = o || ""
|
30
|
+
end
|
31
|
+
opts.on("-s", "--strategy [STRATEGY]",
|
32
|
+
"The gem mirroring strategy used",
|
33
|
+
"Can be: all, latest, missing, latest_missing") do |o|
|
34
|
+
options[:strategy] = o.to_sym
|
35
|
+
end
|
36
|
+
options[:pretend] = false
|
37
|
+
opts.on("-x", "--pretend", TrueClass, "Pretend and just print output") do |o|
|
38
|
+
options[:pretend] = o
|
39
|
+
end
|
40
|
+
options[:no_docs] = true
|
41
|
+
opts.on("-d", "--no-docs", TrueClass, "Don't install with Ri+Rdoc") do |o|
|
42
|
+
options[:no_docs] = o
|
43
|
+
end
|
44
|
+
options[:no_deps] = false
|
45
|
+
opts.on("-i", "--ignore-deps", TrueClass, "Ignore dependencies") do |o|
|
46
|
+
options[:no_deps] = o
|
47
|
+
end
|
48
|
+
|
49
|
+
end.parse!
|
50
|
+
|
51
|
+
GemThief::Thief.run(options)
|
@@ -0,0 +1,92 @@
|
|
1
|
+
gem "net-ssh", ">2.0"
|
2
|
+
require 'net/ssh'
|
3
|
+
module GemThief
|
4
|
+
class Export
|
5
|
+
include GemThief::Thief
|
6
|
+
|
7
|
+
|
8
|
+
def initialize(import, options = {})
|
9
|
+
@import = import
|
10
|
+
@host = options[:host]
|
11
|
+
@user = options[:user]
|
12
|
+
@password = options[:password] || ""
|
13
|
+
end
|
14
|
+
|
15
|
+
def mirror_gems(options = {})
|
16
|
+
mirror_sources
|
17
|
+
gems = case options[:strategy]
|
18
|
+
when :all
|
19
|
+
@import.gem_list
|
20
|
+
when :latest
|
21
|
+
@import.latest_gem_list
|
22
|
+
when :missing
|
23
|
+
missing_gems
|
24
|
+
when :latest_missing
|
25
|
+
latest_missing_gems
|
26
|
+
end
|
27
|
+
connect do |ssh|
|
28
|
+
gems.each do |gem|
|
29
|
+
begin
|
30
|
+
if options[:pretend]
|
31
|
+
puts "Would have mirrored #{gem[0]} version #{gem[1]}"
|
32
|
+
else
|
33
|
+
ssh.exec!(gem_command(gem, options))
|
34
|
+
puts "Mirrored #{gem[0]} version #{gem[1]}"
|
35
|
+
end
|
36
|
+
rescue
|
37
|
+
puts "Error mirroring #{gem[0]} version #{gem[1]}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def sources
|
44
|
+
connect do |ssh|
|
45
|
+
return clean_sources(ssh.exec!("gem sources"))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def installed_gems
|
50
|
+
connect do |ssh|
|
51
|
+
return list_from_cli_source(ssh.exec!("gem list --local"))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def missing_gems
|
56
|
+
@import.gem_list - installed_gems
|
57
|
+
end
|
58
|
+
|
59
|
+
def latest_missing_gems
|
60
|
+
@import.latest_gem_list - installed_gems
|
61
|
+
end
|
62
|
+
|
63
|
+
def missing_sources
|
64
|
+
@import.sources - sources
|
65
|
+
end
|
66
|
+
|
67
|
+
def mirror_sources
|
68
|
+
connect do |ssh|
|
69
|
+
missing_sources.each do |source|
|
70
|
+
ssh.exec!("gem sources -a #{source}")
|
71
|
+
ssh.exec!("gem sources -u #{source}")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def gem_command(gem, options = {})
|
79
|
+
gem_command = "gem install #{gem[0]} -v #{gem[1]}"
|
80
|
+
gem_command << " --no-rdoc --no-ri" if options[:no_docs]
|
81
|
+
gem_command << " --ignore-dependencies" if options[:no_deps]
|
82
|
+
return gem_command
|
83
|
+
end
|
84
|
+
|
85
|
+
def connect(&block)
|
86
|
+
Net::SSH.start(@host, @user, :password => @password) do |ssh|
|
87
|
+
yield ssh
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
module GemThief
|
3
|
+
class Import
|
4
|
+
include GemThief::Thief
|
5
|
+
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@gem_home = File.expand_path Gem.dir
|
9
|
+
@spec_dir = File.join @gem_home, 'specifications'
|
10
|
+
end
|
11
|
+
|
12
|
+
def sources
|
13
|
+
clean_sources(`gem sources`)
|
14
|
+
end
|
15
|
+
|
16
|
+
def gem_list
|
17
|
+
@gem_list ||= t = []
|
18
|
+
source_index.each {|name,x| t << [x.name, x.version]} if @gem_list.empty?
|
19
|
+
@gem_list
|
20
|
+
end
|
21
|
+
|
22
|
+
def latest_gem_list
|
23
|
+
@latest_gem_list ||= source_index.latest_specs.collect {|x| [x.name, x.version]}
|
24
|
+
end
|
25
|
+
|
26
|
+
def source_index
|
27
|
+
@source_index ||= Gem::SourceIndex.from_gems_in @spec_dir
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/gem_thief.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
module GemThief
|
2
|
+
|
3
|
+
# :stopdoc:
|
4
|
+
VERSION = '0.0.1'
|
5
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
6
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
7
|
+
# :startdoc:
|
8
|
+
|
9
|
+
# Returns the version string for the library.
|
10
|
+
#
|
11
|
+
def self.version
|
12
|
+
VERSION
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns the library path for the module. If any arguments are given,
|
16
|
+
# they will be joined to the end of the libray path using
|
17
|
+
# <tt>File.join</tt>.
|
18
|
+
#
|
19
|
+
def self.libpath( *args )
|
20
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the lpath for the module. If any arguments are given,
|
24
|
+
# they will be joined to the end of the path using
|
25
|
+
# <tt>File.join</tt>.
|
26
|
+
#
|
27
|
+
def self.path( *args )
|
28
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Utility method used to rquire all files ending in .rb that lie in the
|
32
|
+
# directory below this file that has the same name as the filename passed
|
33
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
34
|
+
# the _filename_ does not have to be equivalent to the directory.
|
35
|
+
#
|
36
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
37
|
+
dir ||= ::File.basename(fname, '.*')
|
38
|
+
search_me = ::File.expand_path(
|
39
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
40
|
+
|
41
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
42
|
+
end
|
43
|
+
|
44
|
+
end # module GemThief
|
45
|
+
|
46
|
+
require 'rubygems'
|
47
|
+
require GemThief.libpath + '/thief'
|
48
|
+
GemThief.require_all_libs_relative_to(__FILE__)
|
49
|
+
|
50
|
+
# EOF
|
data/lib/thief.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module GemThief
|
2
|
+
module Thief
|
3
|
+
|
4
|
+
def self.run(options = {})
|
5
|
+
import = Import.new
|
6
|
+
export = Export.new(import, options)
|
7
|
+
export.mirror_gems(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def clean_sources(sources_string)
|
11
|
+
return sources_string.split.delete_if {|x| x !~ /^http.*/}
|
12
|
+
end
|
13
|
+
|
14
|
+
def list_from_cli_source(cli_source)
|
15
|
+
raw_list = cli_source.split("\n").delete_if { |x| x == "" || x =~ /\*\*\* (LOCAL|REMOTE) GEMS \*\*\*/ }
|
16
|
+
split_list = raw_list.collect {|line| [line.split(" (")[0], line.split(" (")[1].gsub(")", "").split(", ")] }
|
17
|
+
gem_list = []
|
18
|
+
split_list.each do |gem|
|
19
|
+
gem[1].each do |version|
|
20
|
+
gem_list << [gem[0], version]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return gem_list || []
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
require File.expand_path(
|
3
|
+
File.join(File.dirname(__FILE__), %w[.. lib gem-thief]))
|
4
|
+
|
5
|
+
Spec::Runner.configure do |config|
|
6
|
+
# == Mock Framework
|
7
|
+
#
|
8
|
+
# RSpec uses it's own mocking framework by default. If you prefer to
|
9
|
+
# use mocha, flexmock or RR, uncomment the appropriate line:
|
10
|
+
#
|
11
|
+
# config.mock_with :mocha
|
12
|
+
# config.mock_with :flexmock
|
13
|
+
# config.mock_with :rr
|
14
|
+
end
|
15
|
+
|
16
|
+
# EOF
|
File without changes
|
metadata
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: davidsmalley-gem-thief
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Smalley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-11-01 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Mirrors gems across machines
|
17
|
+
email: david[at]davidsmalley.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- History.txt
|
26
|
+
- Manifest.txt
|
27
|
+
- README.txt
|
28
|
+
- Rakefile
|
29
|
+
- bin/thief
|
30
|
+
- lib/gem_thief.rb
|
31
|
+
- lib/gem_thief/export.rb
|
32
|
+
- lib/gem_thief/import.rb
|
33
|
+
- lib/thief.rb
|
34
|
+
- spec/gem_thief_spec.rb
|
35
|
+
- spec/spec_helper.rb
|
36
|
+
- test/test_gem-thief.rb
|
37
|
+
has_rdoc: false
|
38
|
+
homepage: http://github.com/davidsmalley/gem-thief
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
- lib/gem_thief
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.2.0
|
61
|
+
signing_key:
|
62
|
+
specification_version: 2
|
63
|
+
summary: Mirrors gems across machines
|
64
|
+
test_files: []
|
65
|
+
|