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
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'bits/command'
|
2
|
+
require 'bits/logging'
|
3
|
+
require 'bits/exceptions'
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Bits
|
8
|
+
define_command :provider_query, \
|
9
|
+
:switch => 'provider-query', \
|
10
|
+
:desc => "Query for an atom using the specified provider" \
|
11
|
+
do
|
12
|
+
include Bits::Logging
|
13
|
+
|
14
|
+
def setup(opts)
|
15
|
+
opts.banner = "Usage: bits query <provider> <atom>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def entry(args)
|
19
|
+
if args.size != 2
|
20
|
+
raise InvalidArgument.new "Expected two arguments"
|
21
|
+
end
|
22
|
+
|
23
|
+
provider_id, atom = args
|
24
|
+
provider_id = provider_id.to_sym
|
25
|
+
|
26
|
+
providers = ns[:providers]
|
27
|
+
|
28
|
+
provider = find_provider providers, provider_id
|
29
|
+
|
30
|
+
raise "No such provider: #{provider_id}" if provider.nil?
|
31
|
+
|
32
|
+
puts provider.query(atom)
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_provider(providers, provider_id)
|
36
|
+
providers.each do |provider|
|
37
|
+
return provider if provider.provider_id == provider_id
|
38
|
+
end
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'bits/command'
|
2
|
+
require 'bits/logging'
|
3
|
+
require 'bits/exceptions'
|
4
|
+
|
5
|
+
module Bits
|
6
|
+
define_command :provider_sync, \
|
7
|
+
:switch => 'provider-sync', \
|
8
|
+
:desc => "Sync the specified provider" \
|
9
|
+
do
|
10
|
+
include Bits::Logging
|
11
|
+
|
12
|
+
def setup(opts)
|
13
|
+
opts.banner = "Usage: bits #{switch} <provider>"
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "Providers:"
|
16
|
+
|
17
|
+
ns[:providers].each do |provider|
|
18
|
+
opts.separator " #{provider.provider_id}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def entry(args)
|
23
|
+
if args.size != 1
|
24
|
+
raise InvalidArgument.new "Expected a single argument"
|
25
|
+
end
|
26
|
+
|
27
|
+
provider_id = args.first
|
28
|
+
provider_id = provider_id.to_sym
|
29
|
+
|
30
|
+
providers = ns[:providers]
|
31
|
+
|
32
|
+
provider = find_provider providers, provider_id
|
33
|
+
|
34
|
+
raise "No such provider: #{provider_id}" if provider.nil?
|
35
|
+
|
36
|
+
log.info "Syncing provider: #{provider_id}"
|
37
|
+
|
38
|
+
provider.sync
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_provider(providers, provider_id)
|
42
|
+
providers.each do |provider|
|
43
|
+
return provider if provider.provider_id == provider_id
|
44
|
+
end
|
45
|
+
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/bits/commands/show.rb
CHANGED
@@ -40,7 +40,7 @@ module Bits
|
|
40
40
|
puts " Package Atom: #{ppp.package.atom}"
|
41
41
|
puts " Installed: #{ppp.package.installed_s}"
|
42
42
|
puts " Candidate: #{ppp.package.candidate_s}"
|
43
|
-
puts " Parameters: #{ppp.parameters}"
|
43
|
+
puts " Parameters: #{ppp.parameters.inspect}"
|
44
44
|
end
|
45
45
|
|
46
46
|
return 0
|
data/lib/bits/commands/sync.rb
CHANGED
@@ -5,30 +5,33 @@ require 'bits/exceptions'
|
|
5
5
|
require 'fileutils'
|
6
6
|
|
7
7
|
module Bits
|
8
|
-
define_command :sync,
|
8
|
+
define_command :sync, \
|
9
|
+
:desc => "sync local repository" \
|
10
|
+
do
|
9
11
|
include Bits::Logging
|
10
12
|
|
11
13
|
GIT = 'git'
|
12
14
|
CLONE_URL = 'https://github.com/udoprog/bits-repo'
|
13
15
|
|
14
16
|
def setup(opts)
|
15
|
-
opts.banner = "Usage: bits
|
17
|
+
opts.banner = "Usage: bits #{switch}"
|
16
18
|
opts.separator ""
|
17
19
|
opts.separator "Sync local repository"
|
18
20
|
end
|
19
21
|
|
20
22
|
def entry(args)
|
21
|
-
|
23
|
+
repo_dir = ns[:repo_dir]
|
24
|
+
providers = ns[:providers]
|
22
25
|
|
23
|
-
|
26
|
+
clone repo_dir unless File.directory? repo_dir
|
24
27
|
|
25
|
-
Dir.chdir(
|
28
|
+
Dir.chdir(repo_dir) do
|
26
29
|
Bits.spawn [GIT, 'pull', 'origin', 'master']
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
30
|
-
def
|
31
|
-
Bits.spawn [GIT, 'clone', CLONE_URL,
|
33
|
+
def clone(repo_dir)
|
34
|
+
Bits.spawn [GIT, 'clone', CLONE_URL, repo_dir]
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|
data/lib/bits/exceptions.rb
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
module Bits
|
2
2
|
class ProviderException < Exception; end
|
3
3
|
|
4
|
+
class CommandException < Exception; end
|
5
|
+
|
6
|
+
# Is raised when an invalid argument has been passed to a command.
|
7
|
+
# This indicates that the help text for this specific command should
|
8
|
+
# be shown.
|
9
|
+
class InvalidArgument < CommandException; end
|
10
|
+
|
11
|
+
# Indicate that some dependencies are missing
|
12
|
+
class MissingDependencies < CommandException
|
13
|
+
attr_reader :missing
|
14
|
+
|
15
|
+
def initialize(missing)
|
16
|
+
@missing = missing
|
17
|
+
super "Missing dependencies: #{missing_s}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def missing_s
|
21
|
+
@missing.join ', '
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
4
25
|
# Is raised when a package being requested does not exist.
|
5
26
|
class MissingPackage < ProviderException; end
|
6
27
|
|
data/lib/bits/execute_context.rb
CHANGED
@@ -8,7 +8,7 @@ module Bits
|
|
8
8
|
class Interface
|
9
9
|
include Bits::Logging
|
10
10
|
|
11
|
-
attr_reader :capabilities
|
11
|
+
attr_reader :id, :capabilities, :exitstatus
|
12
12
|
|
13
13
|
def initialize(id, args, stdin, stdout, pid)
|
14
14
|
@id = id
|
@@ -17,13 +17,13 @@ module Bits
|
|
17
17
|
@stdout = stdout
|
18
18
|
@pid = pid
|
19
19
|
@capabilities = []
|
20
|
+
@exitstatus = nil
|
20
21
|
end
|
21
22
|
|
22
23
|
# end the child process by closing stdin.
|
23
24
|
def end
|
24
25
|
@stdin.close
|
25
|
-
|
26
|
-
$?
|
26
|
+
reap_child
|
27
27
|
end
|
28
28
|
|
29
29
|
def info(atom)
|
@@ -71,14 +71,20 @@ module Bits
|
|
71
71
|
private
|
72
72
|
|
73
73
|
def reap_child
|
74
|
-
|
75
|
-
|
74
|
+
return unless @exitstatus.nil?
|
75
|
+
|
76
|
+
log.debug "Reaping interface: #{id}"
|
77
|
+
return unless @exitstatus.nil?
|
78
|
+
log.debug "stdin=#{@stdin.inspect} stdout=#{@stdout.inspect}"
|
76
79
|
|
77
|
-
|
78
|
-
|
80
|
+
Process::kill "INT", @pid
|
81
|
+
|
82
|
+
log.debug "Waiting for interface to exit: #{id}"
|
79
83
|
|
80
84
|
# don't hang since write might have reaped it already.
|
81
|
-
Process.wait @pid
|
85
|
+
Process.wait @pid
|
86
|
+
|
87
|
+
@exitstatus = $?.exitstatus
|
82
88
|
end
|
83
89
|
|
84
90
|
def write(data)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'highline'
|
2
|
+
|
3
|
+
module Bits::InstallerMixin
|
4
|
+
def setup_installer_opts opts
|
5
|
+
opts.on('--[no-]compiled', "Insist on installing an already compiled variant or not") do |v|
|
6
|
+
ns[:compiled] = v
|
7
|
+
end
|
8
|
+
|
9
|
+
opts.on('--force', "Insist on installing even if packages already installed") do |v|
|
10
|
+
ns[:force] = v
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def install_package package, force=false
|
15
|
+
atom = package.atom
|
16
|
+
|
17
|
+
if package.installed? and not force
|
18
|
+
log.info "Already installed '#{atom}' using provider(s): #{package.providers_s}"
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
matching = package.matching_ppps
|
23
|
+
|
24
|
+
raise "No matching PPP could be found" if matching.empty?
|
25
|
+
|
26
|
+
ppp = pick_one atom, matching
|
27
|
+
|
28
|
+
install_ppp atom, ppp
|
29
|
+
end
|
30
|
+
|
31
|
+
# Resolve a hash of dependencies with a criteria to packages.
|
32
|
+
# Returns [<packages>, error]
|
33
|
+
def resolve_packages(depends, criteria)
|
34
|
+
repository = ns[:repository]
|
35
|
+
|
36
|
+
raise "No repository in namespace" if repository.nil?
|
37
|
+
|
38
|
+
packages = Array.new
|
39
|
+
missing = Array.new
|
40
|
+
|
41
|
+
unless depends.kind_of? Array
|
42
|
+
raise ":depends should be a List"
|
43
|
+
end
|
44
|
+
|
45
|
+
depends.each do |spec|
|
46
|
+
unless spec.kind_of? Hash
|
47
|
+
raise "dependency should be of type Hash"
|
48
|
+
end
|
49
|
+
|
50
|
+
atom = spec[:atom]
|
51
|
+
|
52
|
+
if atom.nil?
|
53
|
+
raise "dependency must specify :atom"
|
54
|
+
end
|
55
|
+
|
56
|
+
crit = Hash.new
|
57
|
+
crit = criteria[:compiled] unless criteria[:compiled].nil?
|
58
|
+
crit = spec[:compiled] unless spec[:compiled].nil?
|
59
|
+
|
60
|
+
begin
|
61
|
+
packages << repository.find_package(atom, crit)
|
62
|
+
rescue Bits::MissingBit
|
63
|
+
missing << atom
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
unless missing.empty?
|
68
|
+
raise Bits::MissingDependencies.new missing
|
69
|
+
end
|
70
|
+
|
71
|
+
packages
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def pick_one(atom, matching)
|
77
|
+
return matching[0] if matching.size == 1
|
78
|
+
|
79
|
+
hl = HighLine.new $stdin
|
80
|
+
|
81
|
+
hl.choose do |menu|
|
82
|
+
menu.prompt = "Which provider would you like to install '#{atom}' with?"
|
83
|
+
matching.each do |match|
|
84
|
+
menu.choice(match.provider.provider_id) { match }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def install_ppp(atom, ppp)
|
90
|
+
provider = ppp.provider
|
91
|
+
package = ppp.package
|
92
|
+
|
93
|
+
log.info "Installing '#{atom}' using provider: #{provider.provider_id}"
|
94
|
+
provider.install package
|
95
|
+
end
|
96
|
+
end
|
data/lib/bits/package_proxy.rb
CHANGED
@@ -5,9 +5,10 @@ module Bits
|
|
5
5
|
# This allows for eager loading and lazy lookup of if a specific atom can be
|
6
6
|
# considered as 'installed' or not.
|
7
7
|
class PackageProxy
|
8
|
-
attr_accessor :ppps, :criteria
|
8
|
+
attr_accessor :atom, :ppps, :criteria
|
9
9
|
|
10
|
-
def initialize(ppps, criteria)
|
10
|
+
def initialize(atom, ppps, criteria)
|
11
|
+
@atom = atom
|
11
12
|
@ppps = ppps
|
12
13
|
@criteria = criteria
|
13
14
|
end
|
data/lib/bits/provider.rb
CHANGED
@@ -16,6 +16,14 @@ module Bits
|
|
16
16
|
@ns = ns
|
17
17
|
end
|
18
18
|
|
19
|
+
def setup
|
20
|
+
raise "not implemented: setup"
|
21
|
+
end
|
22
|
+
|
23
|
+
def sync
|
24
|
+
raise "not implemented: sync"
|
25
|
+
end
|
26
|
+
|
19
27
|
def query(atom)
|
20
28
|
raise "not implemented: query"
|
21
29
|
end
|
@@ -64,7 +72,7 @@ module Bits
|
|
64
72
|
attr_reader :provider_id, :desc, :name
|
65
73
|
|
66
74
|
def to_s
|
67
|
-
"
|
75
|
+
"Bits::Provider::#{@name}"
|
68
76
|
end
|
69
77
|
end
|
70
78
|
end
|
data/lib/bits/provider/apt.rb
CHANGED
@@ -1,14 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require 'global'
|
6
|
-
require 'formula'
|
7
|
-
true
|
8
|
-
rescue LoadError
|
9
|
-
$:.replace $old
|
10
|
-
false
|
11
|
-
end
|
1
|
+
require 'bits/logging'
|
2
|
+
require 'bits/command_provider'
|
3
|
+
require 'bits/provider_reporting'
|
4
|
+
require 'bits/external_interface'
|
12
5
|
|
13
6
|
module Bits
|
14
7
|
define_provider :homebrew, \
|
@@ -17,27 +10,35 @@ module Bits
|
|
17
10
|
include Bits::Logging
|
18
11
|
include Bits::CommandProvider
|
19
12
|
include Bits::ProviderReporting
|
13
|
+
include Bits::ExternalInterface
|
20
14
|
|
21
|
-
BREW = '
|
15
|
+
BREW = 'brew'
|
22
16
|
|
23
17
|
def self.check
|
24
|
-
unless
|
25
|
-
check_error "
|
18
|
+
unless self.setup_interface :ruby, :capabilities => [:homebrew]
|
19
|
+
check_error "Could not setup required interface"
|
26
20
|
return false
|
27
21
|
end
|
28
22
|
|
29
|
-
log.debug "
|
23
|
+
log.debug "Homebrew is available"
|
30
24
|
true
|
31
25
|
end
|
32
26
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
rescue FormulaUnavailableError
|
37
|
-
raise MissingPackage.new(atom)
|
38
|
-
end
|
27
|
+
def setup
|
28
|
+
@client = interfaces[:ruby]
|
29
|
+
end
|
39
30
|
|
40
|
-
|
31
|
+
def sync
|
32
|
+
log.info "Does not know how to sync homebrew yet"
|
33
|
+
end
|
34
|
+
|
35
|
+
def query(atom)
|
36
|
+
type, info = @client.request :homebrew_info, :package => atom
|
37
|
+
raise MissingPackage.new atom if type == :missing_package
|
38
|
+
raise "Expected info response but got: #{type}" unless type == :info
|
39
|
+
installed = info['installed']
|
40
|
+
candidate = info['candidate']
|
41
|
+
Bits::Package.new(atom, installed, candidate)
|
41
42
|
end
|
42
43
|
|
43
44
|
def install(package)
|