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.
@@ -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
@@ -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
@@ -5,30 +5,33 @@ require 'bits/exceptions'
5
5
  require 'fileutils'
6
6
 
7
7
  module Bits
8
- define_command :sync, :desc => "sync local repository" do
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 sync"
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
- dir = ns[:local_repository_dir]
23
+ repo_dir = ns[:repo_dir]
24
+ providers = ns[:providers]
22
25
 
23
- setup_original dir unless File.directory? dir
26
+ clone repo_dir unless File.directory? repo_dir
24
27
 
25
- Dir.chdir(dir) do
28
+ Dir.chdir(repo_dir) do
26
29
  Bits.spawn [GIT, 'pull', 'origin', 'master']
27
30
  end
28
31
  end
29
32
 
30
- def setup_original(dir)
31
- Bits.spawn [GIT, 'clone', CLONE_URL, dir]
33
+ def clone(repo_dir)
34
+ Bits.spawn [GIT, 'clone', CLONE_URL, repo_dir]
32
35
  end
33
36
  end
34
37
  end
@@ -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
 
@@ -16,6 +16,7 @@ module Bits
16
16
  end
17
17
  end
18
18
 
19
+ # Run a command and return a boolean indicating weither exitcode is 0
19
20
  def run(args, params={})
20
21
  params[:ignore_exitcode] = true
21
22
  spawn(args, params) == 0
@@ -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
- Process.wait @pid
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
- @stdin.close unless @stdin.nil?
75
- @stdout.close unless @stdout.nil?
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
- @stdin = nil
78
- @stdout = nil
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, Process::WNOHANG
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
@@ -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
- "#<Bits::Provider::#{@name}>"
75
+ "Bits::Provider::#{@name}"
68
76
  end
69
77
  end
70
78
  end
@@ -39,6 +39,16 @@ module Bits
39
39
  true
40
40
  end
41
41
 
42
+ def setup; end
43
+
44
+ def sync
45
+ execute do
46
+ unless run [APT_GET, 'update']
47
+ raise "Could not update apt provider"
48
+ end
49
+ end
50
+ end
51
+
42
52
  def query(atom)
43
53
  result = Apt::Cache::policy(atom)
44
54
 
@@ -1,14 +1,7 @@
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
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 = 'apt-get'
15
+ BREW = 'brew'
22
16
 
23
17
  def self.check
24
- unless HAS_HOMEBREW
25
- check_error "homebrew is not available on this system"
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 "homebrew is available"
23
+ log.debug "Homebrew is available"
30
24
  true
31
25
  end
32
26
 
33
- def query(atom)
34
- begin
35
- f = Formula.factory(atom)
36
- rescue FormulaUnavailableError
37
- raise MissingPackage.new(atom)
38
- end
27
+ def setup
28
+ @client = interfaces[:ruby]
29
+ end
39
30
 
40
- Bits::Package.new(atom, f.installed_version, f.version)
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)