bits-installer 0.1.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.
@@ -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
@@ -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