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.
@@ -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)