bits-installer 0.1.1 → 0.3.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.
- 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)
|