bits-installer 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bits.rb +69 -58
- data/lib/bits/cache.rb +67 -0
- data/lib/bits/command.rb +18 -4
- data/lib/bits/commands/install.rb +10 -43
- data/lib/bits/commands/manifest.rb +71 -0
- data/lib/bits/commands/provider_query.rb +43 -0
- data/lib/bits/commands/provider_sync.rb +49 -0
- data/lib/bits/commands/show.rb +1 -1
- data/lib/bits/commands/sync.rb +10 -7
- data/lib/bits/exceptions.rb +21 -0
- data/lib/bits/execute_context.rb +1 -0
- data/lib/bits/external_interface.rb +14 -8
- data/lib/bits/installer_mixin.rb +96 -0
- data/lib/bits/package_proxy.rb +3 -2
- data/lib/bits/provider.rb +9 -1
- data/lib/bits/provider/apt.rb +10 -0
- data/lib/bits/provider/homebrew.rb +23 -22
- data/lib/bits/provider/portage.rb +2 -2
- data/lib/bits/provider/python.rb +89 -15
- data/lib/bits/provider/rubygems.rb +88 -0
- data/lib/bits/repository.rb +1 -1
- data/lib/bits/version.rb +1 -1
- data/lib/libexec/bits-python +10 -10
- data/lib/libexec/bits-ruby +198 -0
- metadata +9 -2
data/lib/bits.rb
CHANGED
@@ -1,40 +1,44 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
|
3
|
-
require 'bits/backend/local'
|
4
3
|
require 'bits/backend/join'
|
4
|
+
require 'bits/backend/local'
|
5
|
+
require 'bits/external_interface'
|
6
|
+
require 'bits/logging'
|
5
7
|
require 'bits/package'
|
6
8
|
require 'bits/repository'
|
9
|
+
require 'bits/user'
|
7
10
|
|
8
11
|
require 'bits/commands/install'
|
9
12
|
require 'bits/commands/remove'
|
13
|
+
require 'bits/commands/setup'
|
10
14
|
require 'bits/commands/show'
|
11
15
|
require 'bits/commands/sync'
|
12
|
-
require 'bits/commands/
|
16
|
+
require 'bits/commands/query'
|
17
|
+
require 'bits/commands/manifest'
|
18
|
+
require 'bits/commands/provider_query'
|
19
|
+
require 'bits/commands/provider_sync'
|
13
20
|
|
14
|
-
require 'bits/provider/python'
|
15
21
|
require 'bits/provider/apt'
|
16
|
-
require 'bits/provider/portage'
|
17
22
|
require 'bits/provider/homebrew'
|
18
|
-
|
19
|
-
require 'bits/
|
20
|
-
require 'bits/
|
23
|
+
require 'bits/provider/portage'
|
24
|
+
require 'bits/provider/python'
|
25
|
+
require 'bits/provider/rubygems'
|
21
26
|
|
22
27
|
module Bits
|
23
28
|
class << self
|
29
|
+
DEFAULT_COMMAND = 'manifest'
|
30
|
+
|
24
31
|
def parse_options(args)
|
25
32
|
ns = {}
|
26
33
|
|
27
|
-
|
28
|
-
|
29
|
-
|
34
|
+
avail_commands = Bits.commands.values.sort_by{|c| c.command_id.to_s}
|
35
|
+
avail_providers = Bits.providers.values.sort_by{|p| p.provider_id.to_s}
|
36
|
+
|
37
|
+
commands = Hash.new
|
30
38
|
|
31
39
|
global = OptionParser.new do |global_opts|
|
32
40
|
global_opts.banner = "Usage: bits <command> [options]"
|
33
41
|
|
34
|
-
global_opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
35
|
-
ns[:verbose] = v
|
36
|
-
end
|
37
|
-
|
38
42
|
global_opts.on("-d", "--debug", "Enable debug logging") do |v|
|
39
43
|
@log.level = Log4r::DEBUG
|
40
44
|
end
|
@@ -42,91 +46,94 @@ module Bits
|
|
42
46
|
global_opts.separator ""
|
43
47
|
global_opts.separator "Available commands:"
|
44
48
|
|
45
|
-
|
46
|
-
global_opts.separator " #{klass.
|
47
|
-
|
49
|
+
avail_commands.each do |klass|
|
50
|
+
global_opts.separator " #{klass.switch}: #{klass.desc}"
|
51
|
+
commands[klass.switch] = klass
|
48
52
|
end
|
49
53
|
|
50
|
-
|
51
|
-
if provider_class.check
|
52
|
-
available_providers << provider_class
|
53
|
-
else
|
54
|
-
unavailable_providers << provider_class
|
55
|
-
end
|
56
|
-
end
|
54
|
+
global_opts.separator ""
|
57
55
|
|
58
56
|
global_opts.separator "Providers:"
|
59
57
|
|
60
|
-
|
61
|
-
global_opts.separator " #{
|
62
|
-
end
|
63
|
-
|
64
|
-
global_opts.separator "Unavailable providers:"
|
65
|
-
unavailable_providers.each do |klass|
|
66
|
-
global_opts.separator " #{klass.provider_id}: #{klass.last_check_error}"
|
58
|
+
avail_providers.each do |klass|
|
59
|
+
global_opts.separator " #{klass.provider_id}: #{klass.desc}"
|
67
60
|
end
|
68
61
|
end
|
69
62
|
|
70
63
|
global.order!
|
71
64
|
command = ARGV.shift
|
72
65
|
|
66
|
+
checked_providers = avail_providers.select do |klass|
|
67
|
+
@log.debug "Checking provider: #{klass.provider_id}"
|
68
|
+
klass.check
|
69
|
+
end
|
70
|
+
|
71
|
+
command = DEFAULT_COMMAND if command.nil?
|
72
|
+
|
73
73
|
if command.nil? then
|
74
74
|
$stderr.puts global.help
|
75
75
|
exit 0
|
76
76
|
end
|
77
77
|
|
78
|
-
|
79
|
-
|
80
|
-
if subcommands[command].nil? then
|
78
|
+
if commands[command].nil? then
|
81
79
|
$stderr.puts "No such command: #{command}"
|
82
80
|
$stderr.puts global.help
|
83
81
|
exit 0
|
84
82
|
end
|
85
83
|
|
86
|
-
|
87
|
-
|
84
|
+
bits_dir = setup_bits_dir
|
85
|
+
repo_dir = setup_repo_dir bits_dir
|
86
|
+
backend = setup_backend repo_dir
|
88
87
|
|
89
|
-
|
90
|
-
|
88
|
+
ns[:user] = setup_user
|
89
|
+
ns[:bits_dir] = bits_dir
|
90
|
+
ns[:repo_dir] = repo_dir
|
91
|
+
|
92
|
+
providers = checked_providers.collect do |klass|
|
93
|
+
provider = klass.new ns
|
94
|
+
provider.setup
|
95
|
+
provider
|
91
96
|
end
|
92
97
|
|
93
|
-
|
98
|
+
repository = Bits::Repository.new(providers, backend)
|
94
99
|
|
95
|
-
ns[:
|
96
|
-
ns[:
|
100
|
+
ns[:providers] = providers
|
101
|
+
ns[:repository] = repository
|
97
102
|
|
103
|
+
command_klass = commands[command]
|
98
104
|
command = command_klass.new ns
|
99
|
-
providers = setup_providers available_providers, ns
|
100
|
-
backend = setup_backend ns
|
101
105
|
|
102
|
-
|
106
|
+
command_parser = OptionParser.new do |opts|
|
107
|
+
command.setup opts
|
108
|
+
end
|
109
|
+
|
110
|
+
command_parser.order!
|
103
111
|
|
104
|
-
return ARGV, command
|
112
|
+
return ARGV, command, command_parser
|
105
113
|
end
|
106
114
|
|
107
|
-
|
108
|
-
def setup_local_repository_dir(ns)
|
115
|
+
def setup_bits_dir
|
109
116
|
home = ENV['HOME']
|
110
117
|
raise "HOME environment variable not defined" if home.nil?
|
111
|
-
File.join home, '.bits'
|
118
|
+
bits_dir = File.join home, '.bits'
|
119
|
+
FileUtils.mkdir_p bits_dir unless File.directory? bits_dir
|
120
|
+
bits_dir
|
121
|
+
end
|
122
|
+
|
123
|
+
# Setup the path to the local repository directory.
|
124
|
+
def setup_repo_dir(bits_dir)
|
125
|
+
File.join bits_dir, 'bits'
|
112
126
|
end
|
113
127
|
|
114
128
|
def setup_logging
|
115
129
|
log = Log4r::Logger.new 'Bits'
|
116
130
|
log.outputters << Log4r::Outputter.stdout
|
117
|
-
log.level =
|
131
|
+
log.level = Log4r::INFO
|
118
132
|
log
|
119
133
|
end
|
120
134
|
|
121
|
-
def
|
122
|
-
available_providers.collect do |provider_class|
|
123
|
-
provider_class.new ns
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def setup_backend(ns)
|
135
|
+
def setup_backend(local_dir)
|
128
136
|
cwd_dir = File.join Dir.pwd, 'bits'
|
129
|
-
local_dir = ns[:local_repository_dir]
|
130
137
|
|
131
138
|
backends = Array.new
|
132
139
|
|
@@ -139,10 +146,14 @@ module Bits
|
|
139
146
|
def main(args)
|
140
147
|
@log = setup_logging
|
141
148
|
|
142
|
-
args, command = parse_options(args)
|
149
|
+
args, command, command_parser = parse_options(args)
|
143
150
|
|
144
151
|
begin
|
145
152
|
command.entry args
|
153
|
+
rescue InvalidArgument => e
|
154
|
+
$stderr.puts "Argument Error: #{e}"
|
155
|
+
$stderr.puts command_parser.help
|
156
|
+
return 1
|
146
157
|
ensure
|
147
158
|
Bits::ExternalInterface.close_interfaces
|
148
159
|
end
|
data/lib/bits/cache.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Bits::Cache
|
4
|
+
class FileCache
|
5
|
+
attr_reader :cache
|
6
|
+
|
7
|
+
def initialize(path)
|
8
|
+
@path = path
|
9
|
+
@cache = Hash.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def bucket_for(bucket_name)
|
13
|
+
bucket = cache[bucket_name]
|
14
|
+
return bucket unless bucket.nil?
|
15
|
+
cache[bucket_name] = load_bucket bucket_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
bucket = bucket_for(bucket_name key)
|
20
|
+
bucket[key] = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](key)
|
24
|
+
bucket = bucket_for(bucket_name key)
|
25
|
+
return nil if bucket.nil?
|
26
|
+
bucket[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
def set(content)
|
30
|
+
content.each do |key, value|
|
31
|
+
self[key] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def save
|
36
|
+
FileUtils.mkdir_p @path unless File.directory? @path
|
37
|
+
|
38
|
+
@cache.each do |bucket_name, bucket|
|
39
|
+
path = File.join @path, "#{bucket_name}.yml"
|
40
|
+
|
41
|
+
File.open path, 'w' do |f|
|
42
|
+
YAML.dump bucket, f
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def bucket_name(string)
|
48
|
+
string[0..1].each_byte.map { |b| sprintf("%02x",b) }.join
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def load_bucket(bucket_name)
|
54
|
+
path = File.join @path, "#{bucket_name}.yml"
|
55
|
+
|
56
|
+
return {} unless File.file? path
|
57
|
+
|
58
|
+
File.open path do |f|
|
59
|
+
return YAML.load_file f
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def setup_cache(directory, cache_id)
|
65
|
+
FileCache.new File.join(directory, cache_id.to_s)
|
66
|
+
end
|
67
|
+
end
|
data/lib/bits/command.rb
CHANGED
@@ -5,12 +5,16 @@ module Bits
|
|
5
5
|
class Command
|
6
6
|
attr_reader :ns
|
7
7
|
|
8
|
+
def initialize(ns)
|
9
|
+
@ns = ns
|
10
|
+
end
|
11
|
+
|
8
12
|
def setup(opts)
|
9
13
|
raise "not implemented: setup"
|
10
14
|
end
|
11
15
|
|
12
|
-
def
|
13
|
-
|
16
|
+
def entry(args)
|
17
|
+
raise "not implemented: entry"
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
@@ -25,17 +29,27 @@ module Bits
|
|
25
29
|
end
|
26
30
|
|
27
31
|
desc = params[:desc] || "(no description)"
|
32
|
+
switch = params[:switch] || command_id.to_s
|
28
33
|
|
29
34
|
klass = Class.new(Command) do
|
30
35
|
@command_id = command_id
|
31
36
|
@name = command_id.to_s.capitalize
|
32
37
|
@desc = desc
|
38
|
+
@switch = switch
|
39
|
+
|
40
|
+
def switch
|
41
|
+
self.class.switch
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
self.class.to_s
|
46
|
+
end
|
33
47
|
|
34
48
|
class << self
|
35
|
-
attr_reader :command_id, :name, :desc
|
49
|
+
attr_reader :command_id, :name, :desc, :switch
|
36
50
|
|
37
51
|
def to_s
|
38
|
-
"Bits::#{@name}"
|
52
|
+
"Bits::Command::#{@name}"
|
39
53
|
end
|
40
54
|
end
|
41
55
|
end
|
@@ -1,24 +1,25 @@
|
|
1
1
|
require 'bits/command'
|
2
2
|
require 'bits/logging'
|
3
|
-
|
4
|
-
require 'highline'
|
3
|
+
require 'bits/installer_mixin'
|
5
4
|
|
6
5
|
module Bits
|
7
|
-
define_command :install,
|
6
|
+
define_command :install, \
|
7
|
+
:desc => 'Install a package' \
|
8
|
+
do
|
8
9
|
include Bits::Logging
|
10
|
+
include Bits::InstallerMixin
|
9
11
|
|
10
12
|
def setup(opts)
|
11
13
|
ns[:force] = false
|
12
14
|
ns[:compiled] = nil
|
13
15
|
|
14
|
-
opts.banner = "Usage: bits
|
16
|
+
opts.banner = "Usage: bits #{switch} <bit>"
|
17
|
+
|
15
18
|
opts.on('--[no-]compiled', "Insist on installing an already compiled variant or not") do |v|
|
16
19
|
ns[:compiled] = v
|
17
20
|
end
|
18
21
|
|
19
|
-
opts
|
20
|
-
ns[:force] = v
|
21
|
-
end
|
22
|
+
setup_installer_opts opts
|
22
23
|
end
|
23
24
|
|
24
25
|
def entry(args)
|
@@ -35,42 +36,8 @@ module Bits
|
|
35
36
|
:compiled => ns[:compiled]
|
36
37
|
}
|
37
38
|
|
38
|
-
|
39
|
-
|
40
|
-
if p.installed? and not ns[:force]
|
41
|
-
log.info "Already installed '#{atom}' using provider(s): #{p.providers_s}"
|
42
|
-
return 0
|
43
|
-
end
|
44
|
-
|
45
|
-
matching = p.matching_ppps
|
46
|
-
|
47
|
-
raise "No matching PPP could be found" if matching.empty?
|
48
|
-
|
49
|
-
ppp = pick_one atom, matching
|
50
|
-
|
51
|
-
install_ppp atom, ppp
|
52
|
-
return 0
|
53
|
-
end
|
54
|
-
|
55
|
-
def pick_one(atom, matching)
|
56
|
-
return matching[0] if matching.size == 1
|
57
|
-
|
58
|
-
hl = HighLine.new $stdin
|
59
|
-
|
60
|
-
hl.choose do |menu|
|
61
|
-
menu.prompt = "Which provider would you like to install '#{atom}' with?"
|
62
|
-
matching.each do |match|
|
63
|
-
menu.choice(match.provider.provider_id) { match }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def install_ppp(atom, ppp)
|
69
|
-
provider = ppp.provider
|
70
|
-
package = ppp.package
|
71
|
-
|
72
|
-
log.info "Installing '#{atom}' using provider: #{provider.provider_id}"
|
73
|
-
provider.install package
|
39
|
+
package = repository.find_package atom, criteria
|
40
|
+
install_package package, force=ns[:force]
|
74
41
|
end
|
75
42
|
end
|
76
43
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'bits/command'
|
2
|
+
require 'bits/logging'
|
3
|
+
require 'bits/installer_mixin'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Bits
|
7
|
+
define_command :manifest, :desc => 'Run a manifest (this is the default action)' do
|
8
|
+
include Bits::Logging
|
9
|
+
include Bits::InstallerMixin
|
10
|
+
|
11
|
+
BITS_MANIFEST = "Bits"
|
12
|
+
|
13
|
+
def setup(opts)
|
14
|
+
ns[:compiled] = nil
|
15
|
+
|
16
|
+
opts.banner = "Usage: bits manifest [path]"
|
17
|
+
|
18
|
+
setup_installer_opts opts
|
19
|
+
end
|
20
|
+
|
21
|
+
def entry(args)
|
22
|
+
if args.empty? then
|
23
|
+
path = File.join Dir.pwd, BITS_MANIFEST
|
24
|
+
else
|
25
|
+
path = args.first
|
26
|
+
end
|
27
|
+
|
28
|
+
raise "No manifest in path: #{path}" unless File.file? path
|
29
|
+
|
30
|
+
manifest = YAML.load File.new(path)
|
31
|
+
|
32
|
+
unless manifest.kind_of? Hash
|
33
|
+
raise "Manifest is not of type Hash: #{path}"
|
34
|
+
end
|
35
|
+
|
36
|
+
depends = manifest[:depends]
|
37
|
+
|
38
|
+
criteria = {
|
39
|
+
:compiled => ns[:compiled]
|
40
|
+
}
|
41
|
+
|
42
|
+
log.info "Running manifest: #{path}"
|
43
|
+
|
44
|
+
unless depends.nil?
|
45
|
+
begin
|
46
|
+
packages = resolve_packages depends, criteria
|
47
|
+
rescue Bits::MissingDependencies => e
|
48
|
+
puts "Missing Dependencies:"
|
49
|
+
|
50
|
+
e.missing.each do |m|
|
51
|
+
puts " - #{m}"
|
52
|
+
end
|
53
|
+
|
54
|
+
return 1
|
55
|
+
end
|
56
|
+
|
57
|
+
packages.each do |package|
|
58
|
+
log.info "Installing package: #{package}"
|
59
|
+
install_package package, force=ns[:force]
|
60
|
+
end
|
61
|
+
else
|
62
|
+
log.info "No dependencies specified"
|
63
|
+
end
|
64
|
+
|
65
|
+
return 0
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|