bits-installer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/bits +20 -0
- data/ext/apt/Cache.cpp +68 -0
- data/ext/apt/Cache.h +18 -0
- data/ext/apt/Package.cpp +63 -0
- data/ext/apt/Package.h +30 -0
- data/ext/apt/PackageVersion.cpp +49 -0
- data/ext/apt/PackageVersion.h +24 -0
- data/ext/apt/apt_ext.cpp +38 -0
- data/ext/apt/apt_ext.h +4 -0
- data/ext/apt/extconf.rb +12 -0
- data/lib/bits/backend/join.rb +26 -0
- data/lib/bits/backend/local.rb +24 -0
- data/lib/bits/backend.rb +9 -0
- data/lib/bits/bit.rb +33 -0
- data/lib/bits/bit_declaration.rb +143 -0
- data/lib/bits/bit_reader/local.rb +13 -0
- data/lib/bits/bit_reader.rb +7 -0
- data/lib/bits/command.rb +48 -0
- data/lib/bits/command_provider.rb +39 -0
- data/lib/bits/commands/install.rb +75 -0
- data/lib/bits/commands/remove.rb +35 -0
- data/lib/bits/commands/show.rb +49 -0
- data/lib/bits/commands/sync.rb +34 -0
- data/lib/bits/exceptions.rb +12 -0
- data/lib/bits/execute_context.rb +24 -0
- data/lib/bits/external_interface.rb +204 -0
- data/lib/bits/logging.rb +24 -0
- data/lib/bits/package.rb +27 -0
- data/lib/bits/package_proxy.rb +61 -0
- data/lib/bits/provider/apt.rb +75 -0
- data/lib/bits/provider/homebrew.rb +59 -0
- data/lib/bits/provider/portage.rb +69 -0
- data/lib/bits/provider/python.rb +86 -0
- data/lib/bits/provider.rb +77 -0
- data/lib/bits/provider_reporting.rb +24 -0
- data/lib/bits/repository.rb +118 -0
- data/lib/bits/spawn.rb +89 -0
- data/lib/bits/user.rb +27 -0
- data/lib/bits/version.rb +3 -0
- data/lib/bits.rb +156 -0
- data/lib/libexec/bits-python +157 -0
- metadata +153 -0
data/lib/bits/logging.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
|
3
|
+
module Bits
|
4
|
+
module Logging
|
5
|
+
module ClassMethods
|
6
|
+
def setup_logging(name)
|
7
|
+
@logging_name = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def log
|
11
|
+
@logging_logger ||= Log4r::Logger.new(@logging_name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def log
|
16
|
+
self.class.log
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(mod)
|
20
|
+
mod.extend ClassMethods
|
21
|
+
mod.setup_logging mod.to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/bits/package.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Bits
|
2
|
+
class Package
|
3
|
+
attr_reader :atom, :installed, :candidate
|
4
|
+
|
5
|
+
def initialize(atom, installed, candidate)
|
6
|
+
@atom = atom
|
7
|
+
@installed = installed
|
8
|
+
@candidate = candidate
|
9
|
+
end
|
10
|
+
|
11
|
+
def installed_s
|
12
|
+
return "(not installed)" if installed.nil?
|
13
|
+
installed
|
14
|
+
end
|
15
|
+
|
16
|
+
def candidate_s
|
17
|
+
return "(no candidate)" if candidate.nil?
|
18
|
+
candidate
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
installed_s = @installed || "(not installed)"
|
23
|
+
candidate_s = @candidate || "(no candidate)"
|
24
|
+
"<Package atom='#{@atom}' installed='#{installed_s}' candidate='#{candidate_s}'>"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Bits
|
2
|
+
# Data class used to keep track of all found ppps (package, provider, params)
|
3
|
+
# for a specific bit and the supplied criteria.
|
4
|
+
#
|
5
|
+
# This allows for eager loading and lazy lookup of if a specific atom can be
|
6
|
+
# considered as 'installed' or not.
|
7
|
+
class PackageProxy
|
8
|
+
attr_accessor :ppps, :criteria
|
9
|
+
|
10
|
+
def initialize(ppps, criteria)
|
11
|
+
@ppps = ppps
|
12
|
+
@criteria = criteria
|
13
|
+
end
|
14
|
+
|
15
|
+
# Check if the following set of parameters matches the specified criteria.
|
16
|
+
def matches_criteria?(params)
|
17
|
+
criteria.all?{|key, value| value.nil? or params[key] == value}
|
18
|
+
end
|
19
|
+
|
20
|
+
def dependencies
|
21
|
+
ppps.collect{|ppp| ppp.bit.dependencies}.flatten
|
22
|
+
end
|
23
|
+
|
24
|
+
def matching_ppps
|
25
|
+
ppps.select do |ppp|
|
26
|
+
matches_criteria? ppp.parameters
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Remove the specified package.
|
31
|
+
# The package will be removed from all matching PPPs.
|
32
|
+
def remove
|
33
|
+
ppps.each do |ppp|
|
34
|
+
ppp.provider.remove ppp.package
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Determines if the specified package is installed or not.
|
39
|
+
def installed?
|
40
|
+
ppps.any? do |ppp|
|
41
|
+
not ppp.package.installed.nil?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def installed
|
46
|
+
ppps.select do |ppp|
|
47
|
+
not ppp.package.installed.nil?
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def providers
|
52
|
+
ppps.map do |ppp|
|
53
|
+
ppp.provider
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def providers_s
|
58
|
+
providers.map(&:provider_id).join ', '
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'bits/provider'
|
2
|
+
require 'bits/provider_reporting'
|
3
|
+
require 'bits/package'
|
4
|
+
require 'bits/logging'
|
5
|
+
require 'bits/spawn'
|
6
|
+
require 'bits/exceptions'
|
7
|
+
|
8
|
+
require 'bits/command_provider'
|
9
|
+
|
10
|
+
HAS_APT_NATIVE_EXT = begin
|
11
|
+
require 'apt/apt_ext'
|
12
|
+
true
|
13
|
+
rescue LoadError
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
module Bits
|
18
|
+
define_provider :apt, \
|
19
|
+
:desc => "Provides interface for Debian APT" \
|
20
|
+
do
|
21
|
+
include Bits::Logging
|
22
|
+
include Bits::CommandProvider
|
23
|
+
include Bits::ProviderReporting
|
24
|
+
|
25
|
+
APT_GET = 'apt-get'
|
26
|
+
|
27
|
+
def self.check
|
28
|
+
unless HAS_APT_NATIVE_EXT
|
29
|
+
check_error "APT native extension could not be loaded"
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
unless Apt::initialize
|
34
|
+
check_error "APT native extension could not be initialized"
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
log.debug "APT native extension is available"
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
def query(atom)
|
43
|
+
result = Apt::Cache::policy(atom)
|
44
|
+
|
45
|
+
raise MissingPackage.new atom if result.empty?
|
46
|
+
raise "Too many packages '#{atom}'" if result.size > 1
|
47
|
+
|
48
|
+
package = result[0]
|
49
|
+
|
50
|
+
current = nil
|
51
|
+
candidate = nil
|
52
|
+
|
53
|
+
current = package.current.version if package.current
|
54
|
+
candidate = package.candidate.version if package.candidate
|
55
|
+
|
56
|
+
return Bits::Package.new(package.name, current, candidate)
|
57
|
+
end
|
58
|
+
|
59
|
+
def install(package)
|
60
|
+
execute do
|
61
|
+
unless run [APT_GET, 'install', package.atom], :superuser => true
|
62
|
+
raise "Could not install package '#{package.atom}'"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def remove(package)
|
68
|
+
execute do
|
69
|
+
unless run [APT_GET, 'remove', package.atom], :superuser => true
|
70
|
+
raise "Could not remove package '#{package.atom}'"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
$old = Array.new $:
|
2
|
+
|
3
|
+
HAS_HOMEBREW = begin
|
4
|
+
$: << '/usr/local/Library/Homebrew'
|
5
|
+
require 'global'
|
6
|
+
require 'formula'
|
7
|
+
true
|
8
|
+
rescue LoadError
|
9
|
+
$:.replace $old
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
module Bits
|
14
|
+
define_provider :homebrew, \
|
15
|
+
:desc => "Provides interface for Homebrew" \
|
16
|
+
do
|
17
|
+
include Bits::Logging
|
18
|
+
include Bits::CommandProvider
|
19
|
+
include Bits::ProviderReporting
|
20
|
+
|
21
|
+
BREW = 'apt-get'
|
22
|
+
|
23
|
+
def self.check
|
24
|
+
unless HAS_HOMEBREW
|
25
|
+
check_error "homebrew is not available on this system"
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
log.debug "homebrew is available"
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def query(atom)
|
34
|
+
begin
|
35
|
+
f = Formula.factory(atom)
|
36
|
+
rescue FormulaUnavailableError
|
37
|
+
raise MissingPackage.new(atom)
|
38
|
+
end
|
39
|
+
|
40
|
+
Bits::Package.new(atom, f.installed_version, f.version)
|
41
|
+
end
|
42
|
+
|
43
|
+
def install(package)
|
44
|
+
execute do
|
45
|
+
unless run [BREW, 'install', package.atom]
|
46
|
+
raise "Could not install package '#{package.atom}'"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def remove(package)
|
52
|
+
execute do
|
53
|
+
unless spawn [BREW, 'uninstall', package.atom]
|
54
|
+
raise "Could not remove package '#{package.atom}'"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'bits/provider'
|
2
|
+
require 'bits/provider_reporting'
|
3
|
+
require 'bits/command_provider'
|
4
|
+
require 'bits/package'
|
5
|
+
require 'bits/logging'
|
6
|
+
require 'bits/spawn'
|
7
|
+
|
8
|
+
require 'json'
|
9
|
+
|
10
|
+
module Bits
|
11
|
+
define_provider :portage, \
|
12
|
+
:desc => "Provides interface to Gentoo Portage" \
|
13
|
+
do
|
14
|
+
include Bits::Logging
|
15
|
+
include Bits::CommandProvider
|
16
|
+
include Bits::ExternalInterface
|
17
|
+
include Bits::ProviderReporting
|
18
|
+
|
19
|
+
# bridge command
|
20
|
+
EMERGE = 'emerge'
|
21
|
+
|
22
|
+
def self.check
|
23
|
+
unless self.check_command [EMERGE, '--version'], "EMERGE"
|
24
|
+
check_error "Could not execute '#{EMERGE} --version'"
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
unless self.setup_interface :python, :capabilities => [:portage]
|
29
|
+
check_error "Could not setup require python interface"
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
|
33
|
+
log.debug "portage is available"
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(ns)
|
38
|
+
super ns
|
39
|
+
@client = interfaces[:python]
|
40
|
+
end
|
41
|
+
|
42
|
+
def query(package_name)
|
43
|
+
type, info = @client.request :portage_info, :package => package_name
|
44
|
+
raise "Expected info response but got: #{type}" unless type == :info
|
45
|
+
|
46
|
+
name = info['name']
|
47
|
+
installed = info['installed']
|
48
|
+
candidate = info['candidate']
|
49
|
+
|
50
|
+
return Bits::Package.new(name, installed, candidate)
|
51
|
+
end
|
52
|
+
|
53
|
+
def install(package)
|
54
|
+
execute do
|
55
|
+
unless run [EMERGE, package.atom]
|
56
|
+
raise "Could not install package '#{package.atom}'"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def remove(package)
|
62
|
+
execute do
|
63
|
+
unless run [EMERGE, "--unmerge", package.atom]
|
64
|
+
raise "Could not remove package '#{package.atom}'"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'bits/provider'
|
2
|
+
require 'bits/provider_reporting'
|
3
|
+
require 'bits/command_provider'
|
4
|
+
require 'bits/spawn'
|
5
|
+
require 'bits/package'
|
6
|
+
require 'bits/logging'
|
7
|
+
require 'bits/exceptions'
|
8
|
+
|
9
|
+
require 'bits/external_interface'
|
10
|
+
|
11
|
+
require "xmlrpc/client"
|
12
|
+
|
13
|
+
module Bits
|
14
|
+
define_provider :python,
|
15
|
+
:desc => "Provides interface for Python Packages" \
|
16
|
+
do
|
17
|
+
include Bits::Logging
|
18
|
+
include Bits::CommandProvider
|
19
|
+
include Bits::ExternalInterface
|
20
|
+
include Bits::ProviderReporting
|
21
|
+
|
22
|
+
PIP = 'pip'
|
23
|
+
INDEX = 'https://pypi.python.org/pypi'
|
24
|
+
|
25
|
+
def self.check
|
26
|
+
unless self.check_command [PIP, '--version'], 'PIP'
|
27
|
+
check_error "Could not execute '#{PIP} --version'"
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
|
31
|
+
unless self.setup_interface :python, :capabilities => [:pkg_resources]
|
32
|
+
check_error "Could not setup require python interface"
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
|
36
|
+
log.debug "python extensions is available"
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(ns)
|
41
|
+
super ns
|
42
|
+
@client = XMLRPC::Client.new_from_uri(INDEX)
|
43
|
+
@client.http_header_extra = {'Content-Type' => 'text/xml'}
|
44
|
+
@python = self.class.interfaces[:python]
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_installed_version(package_atom)
|
48
|
+
type, response = @python.request :python_info, :atom => package_atom
|
49
|
+
return nil if type == :missing_atom
|
50
|
+
response['installed']
|
51
|
+
end
|
52
|
+
|
53
|
+
def get_candidate_version(package_atom)
|
54
|
+
begin
|
55
|
+
result = @client.call(:package_releases, package_atom)
|
56
|
+
rescue SocketError
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
|
60
|
+
raise MissingPackage.new package_atom if result.empty?
|
61
|
+
return result[0]
|
62
|
+
end
|
63
|
+
|
64
|
+
def query(package_atom)
|
65
|
+
candidate = get_candidate_version package_atom
|
66
|
+
current = get_installed_version package_atom
|
67
|
+
return Bits::Package.new(package_atom, current, candidate)
|
68
|
+
end
|
69
|
+
|
70
|
+
def install(package)
|
71
|
+
execute do
|
72
|
+
unless run [PIP, 'install', package.atom], :superuser => true
|
73
|
+
raise "Could not install package '#{package.atom}'"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def remove(package)
|
79
|
+
execute do
|
80
|
+
unless run [PIP, 'uninstall', package.atom], :superuser => true
|
81
|
+
raise "Could not remove package '#{package.atom}'"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'bits/execute_context'
|
2
|
+
|
3
|
+
module Bits
|
4
|
+
class Provider
|
5
|
+
class << self
|
6
|
+
# Override to provide custom static initialization code for provider.
|
7
|
+
# Should return true if the specified provider can be used in this system.
|
8
|
+
def check
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :ns
|
14
|
+
|
15
|
+
def initialize(ns)
|
16
|
+
@ns = ns
|
17
|
+
end
|
18
|
+
|
19
|
+
def query(atom)
|
20
|
+
raise "not implemented: query"
|
21
|
+
end
|
22
|
+
|
23
|
+
def install(atom)
|
24
|
+
raise "not implemented: install"
|
25
|
+
end
|
26
|
+
|
27
|
+
def remove(atom)
|
28
|
+
raise "not implemented: remove"
|
29
|
+
end
|
30
|
+
|
31
|
+
def execute(&block)
|
32
|
+
raise "Missing user from namespace" if ns[:user].nil?
|
33
|
+
context = ExecuteContext.new ns[:user]
|
34
|
+
return context.instance_eval(&block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class << self
|
39
|
+
def providers
|
40
|
+
@providers ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def define_provider(provider_id, params={}, &block)
|
44
|
+
if providers[provider_id]
|
45
|
+
raise "Provider already defined: #{provider_id}"
|
46
|
+
end
|
47
|
+
|
48
|
+
desc = params[:desc] || "(no description)"
|
49
|
+
|
50
|
+
klass = Class.new Provider do
|
51
|
+
@provider_id = provider_id
|
52
|
+
@name = provider_id.to_s.capitalize
|
53
|
+
@desc = desc
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
self.class.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
def provider_id
|
60
|
+
self.class.provider_id
|
61
|
+
end
|
62
|
+
|
63
|
+
class << self
|
64
|
+
attr_reader :provider_id, :desc, :name
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
"#<Bits::Provider::#{@name}>"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
klass.class_eval(&block)
|
73
|
+
|
74
|
+
providers[provider_id] = klass
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|