rip 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/.gitignore +4 -0
  2. data/LICENSE +20 -0
  3. data/README.markdown +306 -0
  4. data/Rakefile +51 -0
  5. data/bin/rip +6 -0
  6. data/examples/debug.rb +13 -0
  7. data/examples/reverse.rb +21 -0
  8. data/ext/extconf.rb +11 -0
  9. data/lib/rip.rb +53 -0
  10. data/lib/rip/commands.rb +113 -0
  11. data/lib/rip/commands/build.rb +23 -0
  12. data/lib/rip/commands/core.rb +82 -0
  13. data/lib/rip/commands/install.rb +37 -0
  14. data/lib/rip/commands/uninstall.rb +43 -0
  15. data/lib/rip/env.rb +128 -0
  16. data/lib/rip/installer.rb +130 -0
  17. data/lib/rip/memoize.rb +111 -0
  18. data/lib/rip/package.rb +126 -0
  19. data/lib/rip/package_api.rb +94 -0
  20. data/lib/rip/package_manager.rb +175 -0
  21. data/lib/rip/packages/dir_package.rb +34 -0
  22. data/lib/rip/packages/file_package.rb +60 -0
  23. data/lib/rip/packages/gem_package.rb +44 -0
  24. data/lib/rip/packages/git_package.rb +62 -0
  25. data/lib/rip/packages/http_package.rb +46 -0
  26. data/lib/rip/packages/remote_gem_package.rb +64 -0
  27. data/lib/rip/packages/ripfile_package.rb +46 -0
  28. data/lib/rip/setup.rb +205 -0
  29. data/lib/rip/sh/git.rb +35 -0
  30. data/lib/rip/ui.rb +24 -0
  31. data/lib/rip/version.rb +9 -0
  32. data/setup.rb +27 -0
  33. data/test/commands_test.rb +15 -0
  34. data/test/dev.rip +2 -0
  35. data/test/dir_test.rb +25 -0
  36. data/test/env_test.rb +173 -0
  37. data/test/git_test.rb +36 -0
  38. data/test/mock_git.rb +51 -0
  39. data/test/repos/simple_c/dot_git/HEAD +1 -0
  40. data/test/repos/simple_c/dot_git/config +6 -0
  41. data/test/repos/simple_c/dot_git/description +1 -0
  42. data/test/repos/simple_c/dot_git/hooks/applypatch-msg.sample +15 -0
  43. data/test/repos/simple_c/dot_git/hooks/commit-msg.sample +24 -0
  44. data/test/repos/simple_c/dot_git/hooks/post-commit.sample +8 -0
  45. data/test/repos/simple_c/dot_git/hooks/post-receive.sample +15 -0
  46. data/test/repos/simple_c/dot_git/hooks/post-update.sample +8 -0
  47. data/test/repos/simple_c/dot_git/hooks/pre-applypatch.sample +14 -0
  48. data/test/repos/simple_c/dot_git/hooks/pre-commit.sample +18 -0
  49. data/test/repos/simple_c/dot_git/hooks/pre-rebase.sample +169 -0
  50. data/test/repos/simple_c/dot_git/hooks/prepare-commit-msg.sample +36 -0
  51. data/test/repos/simple_c/dot_git/hooks/update.sample +107 -0
  52. data/test/repos/simple_c/dot_git/index +0 -0
  53. data/test/repos/simple_c/dot_git/info/exclude +6 -0
  54. data/test/repos/simple_c/dot_git/logs/HEAD +1 -0
  55. data/test/repos/simple_c/dot_git/logs/refs/heads/master +1 -0
  56. data/test/repos/simple_c/dot_git/objects/2d/94227280db3ac66875f52592c6a736b4526084 +0 -0
  57. data/test/repos/simple_c/dot_git/objects/3f/1d6dacdedf75058e9edf23f48de03fa451f7ce +1 -0
  58. data/test/repos/simple_c/dot_git/objects/53/23e9a7ff897fe7bfc3357d9274775b67f9ade4 +0 -0
  59. data/test/repos/simple_c/dot_git/objects/55/31db58bd71148661c400dc48815bf06b366128 +0 -0
  60. data/test/repos/simple_c/dot_git/objects/d7/55c6f119520808609a8d79bac1a8dbe0c57424 +0 -0
  61. data/test/repos/simple_c/dot_git/objects/d7/58739ea968ac8e8fe0cbbf1f6451af47f04964 +0 -0
  62. data/test/repos/simple_c/dot_git/objects/e6/7b81a24f0ce4bff84c3599b5c9ba7093f554d8 +0 -0
  63. data/test/repos/simple_c/dot_git/objects/ec/be0a80dc841c16beb2c733bbdd320b45565d89 +0 -0
  64. data/test/repos/simple_c/dot_git/refs/heads/master +1 -0
  65. data/test/repos/simple_c/ext/simp/extconf.rb +6 -0
  66. data/test/repos/simple_c/ext/simp/simp.c +17 -0
  67. data/test/repos/simple_c/lib/simple_c.rb +1 -0
  68. data/test/repos/simple_d-1.2.3/lib/simple_d.rb +1 -0
  69. data/test/rip_test.rb +8 -0
  70. data/test/test_helper.rb +79 -0
  71. data/test/ui_test.rb +36 -0
  72. metadata +149 -0
@@ -0,0 +1,113 @@
1
+ module Rip
2
+ module Commands
3
+ extend self
4
+
5
+ def invoke(args)
6
+ command, options, args = parse_args(args)
7
+
8
+ if command.nil? || command == ''
9
+ command = :help
10
+ end
11
+
12
+ command = find_command(command)
13
+
14
+ begin
15
+ send(command, options, *args)
16
+ rescue => e
17
+ if options[:error]
18
+ raise e
19
+ else
20
+ ui.puts "rip: #{command} failed"
21
+ ui.puts "-> #{e.message}"
22
+ end
23
+ end
24
+ end
25
+
26
+ def public_instance_methods
27
+ super - %w( invoke public_instance_methods )
28
+ end
29
+
30
+ private
31
+ # tasty dsl for adding help text
32
+
33
+ def o(usage)
34
+ @usage ||= {}
35
+ @next_usage = usage
36
+ end
37
+
38
+ def x(help)
39
+ @help ||= {}
40
+ @next_help ||= []
41
+ @next_help.push help
42
+ end
43
+
44
+ def method_added(method)
45
+ @help[method.to_s] = @next_help if @next_help
46
+ @usage[method.to_s] = @next_usage if @next_usage
47
+ @next_help = nil
48
+ @next_usage = nil
49
+ end
50
+
51
+ def ui
52
+ Rip.ui
53
+ end
54
+
55
+ def manager(env = nil)
56
+ @manager ||= PackageManager.new(env)
57
+ end
58
+
59
+ def find_command(command)
60
+ matches = public_instance_methods.select do |method|
61
+ method =~ /^#{command}/
62
+ end
63
+
64
+ if matches.size == 0
65
+ :help
66
+ elsif matches.size == 1
67
+ matches.first
68
+ else
69
+ ui.abort "rip: which command did you mean? #{matches.join(' or ')}"
70
+ end
71
+ end
72
+
73
+ def parse_args(args)
74
+ command = args.shift
75
+ options = args.select { |piece| piece =~ /^-/ }
76
+ args -= options
77
+ options = options.inject({}) do |hash, flag|
78
+ key, value = flag.split('=')
79
+ hash[key.sub(/^--?/,'').intern] = value.nil? ? true : value
80
+ hash
81
+ end
82
+
83
+ [command, options, args]
84
+ end
85
+ end
86
+ end
87
+
88
+
89
+ #
90
+ # rip plugin commands
91
+ #
92
+
93
+ # load ~/.rip/rip-commands/*.rb
94
+ if File.exists? dir = File.join(Rip.dir, 'rip-commands')
95
+ Dir[dir + '/*.rb'].each do |file|
96
+ require file
97
+ end
98
+ end
99
+
100
+ # load lib/rip/commands/*.rb from the active ripenv
101
+ if File.exists? dir = File.join(Rip::Env.active_dir, 'lib', 'rip', 'commands')
102
+ Dir[dir + '/*.rb'].each do |file|
103
+ require file
104
+ end
105
+ end
106
+
107
+
108
+ # load lib/rip/commands/*.rb from rip itself
109
+ if File.exists? dir = File.join(File.dirname(__FILE__), 'commands')
110
+ Dir[dir + '/*.rb'].each do |file|
111
+ require file
112
+ end
113
+ end
@@ -0,0 +1,23 @@
1
+ # file: lib/rip/commands/build.rb
2
+ #
3
+ # rip build
4
+ # Builds Ruby extensions for installed packages
5
+
6
+ module Rip
7
+ module Commands
8
+ def build(options={}, *packages)
9
+ packages.each do |package_name|
10
+ ui.puts "rip: building package: #{package_name}"
11
+ package = manager.package(package_name)
12
+
13
+ Dir["#{package.cache_path}/**/extconf.rb"].each do |build_file|
14
+ build_dir = File.dirname(build_file)
15
+ Dir.chdir(build_dir) {
16
+ system "ruby extconf.rb"
17
+ system "make install RUBYARCHDIR=#{manager.dir}/lib"
18
+ }
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,82 @@
1
+ module Rip
2
+ module Commands
3
+ x 'Display libraries installed in the current ripenv.'
4
+ def list(*args)
5
+ ui.puts 'ripenv: ' + Rip::Env.active, ''
6
+ if manager.packages.any?
7
+ ui.puts manager.packages
8
+ else
9
+ ui.puts "nothing installed"
10
+ end
11
+ end
12
+
13
+ def help(options = {}, command = nil, *args)
14
+ command = command.to_s
15
+ if !command.empty? && respond_to?(command)
16
+ ui.puts "Usage: %s" % (@usage[command] || "rip #{command.downcase}")
17
+ if @help[command]
18
+ ui.puts
19
+ ui.puts(*@help[command])
20
+ end
21
+ else
22
+ show_general_help
23
+ end
24
+ end
25
+
26
+ o 'rip env COMMAND'
27
+ x 'Commands for managing your ripenvs.'
28
+ x 'Type rip env to see valid options.'
29
+ def env(options = {}, command = nil, *args)
30
+ if command && Rip::Env.respond_to?(command)
31
+ ui.puts 'ripenv: ' + Rip::Env.call(command, *args).to_s
32
+ else
33
+ show_help :env, Rip::Env.commands
34
+ ui.puts '', "current ripenv: #{Rip::Env.active}"
35
+ end
36
+ end
37
+
38
+ x 'Outputs all installed libraries (and their versions) for the active env.'
39
+ x 'Can be saved as a .rip and installed by other rip users with:'
40
+ x ' rip install file.rip'
41
+ def freeze(options = {}, env = nil, *args)
42
+ manager(env).packages.each do |package|
43
+ ui.puts "#{package.source} #{package.version}"
44
+ end
45
+ end
46
+
47
+ x 'Prints the current version and exits.'
48
+ def version(options = {}, *args)
49
+ ui.puts "Rip #{Rip::Version}"
50
+ end
51
+ alias_method "-v", :version
52
+ alias_method "--version", :version
53
+
54
+ private
55
+ def show_help(command, commands)
56
+ subcommand = command.to_s.empty? ? nil : "#{command} "
57
+ ui.puts "Usage: rip #{subcommand}COMMAND [options]", ""
58
+ ui.puts "Commands available:"
59
+
60
+ commands.each do |method|
61
+ ui.puts " #{method}"
62
+ end
63
+ end
64
+
65
+ def show_general_help
66
+ commands = public_instance_methods.reject do |method|
67
+ method =~ /-/ || %w( help version ).include?(method)
68
+ end
69
+
70
+ show_help nil, commands
71
+
72
+ ui.puts
73
+ ui.puts "For more information on a a command use:"
74
+ ui.puts " rip help COMMAND"
75
+ ui.puts
76
+
77
+ ui.puts "Options: "
78
+ ui.puts " -h, --help show this help message and exit"
79
+ ui.puts " -v, --version show the current version and exit"
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,37 @@
1
+ module Rip
2
+ module Commands
3
+ x 'Checks that your rip installation is valid.'
4
+ def check(*args)
5
+ Setup.check_installation
6
+ ui.puts "All systems go."
7
+ rescue => e
8
+ ui.abort "Installation failed: #{e.message}"
9
+ end
10
+
11
+ o 'rip install SOURCE [options]'
12
+ x 'Installs a package from SOURCE.'
13
+ x '-f forces installation (overwrites existing)'
14
+ def install(options = {}, source = nil, version = nil, *args)
15
+ if source.to_s.empty?
16
+ ui.abort "Please tell me what to install."
17
+ end
18
+
19
+ package = Rip::Package.for(source, version)
20
+
21
+ if !package
22
+ ui.abort "I don't know how to install #{source}"
23
+ end
24
+
25
+ if options[:f]
26
+ Installer.new.uninstall(package) if package.installed?
27
+ Installer.new.install(package)
28
+ elsif package.installed?
29
+ ui.puts "#{package} already installed"
30
+ else
31
+ installer = Installer.new
32
+ installer.install(package)
33
+ # puts "#{installer.installed.size.to_i} packages installed"
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,43 @@
1
+ module Rip
2
+ module Commands
3
+ o 'rip uninstall PACKAGE [options]'
4
+ x 'Uninstalls a single Rip package.'
5
+ x '-y removes the package no matter what.'
6
+ x '-d removes the package and its dependents.'
7
+ def uninstall(options = {}, name = nil, *args)
8
+ if name.to_s.empty?
9
+ ui.abort "Please tell me what to uninstall."
10
+ end
11
+
12
+ force = options[:y] || options[:d]
13
+ package = manager.package(name)
14
+
15
+ if !package
16
+ ui.abort "#{name} isn't installed."
17
+ end
18
+
19
+ dependents = manager.packages_that_depend_on(name)
20
+
21
+ if dependents.any? && !force
22
+ ui.puts "You have requested to uninstall the package:"
23
+ ui.puts " #{package}"
24
+ ui.puts
25
+ ui.puts "The following packages depend on #{name}:"
26
+
27
+ dependents.each do |dependent|
28
+ ui.puts " #{dependent}"
29
+ end
30
+
31
+ ui.puts
32
+ ui.puts "If you remove this package one or more dependencies will not be met."
33
+ ui.puts "Pass -y if you really want to remove #{name}"
34
+ ui.abort "Pass -d if you want to remove #{name} and its dependents."
35
+ end
36
+
37
+ if force || dependents.empty?
38
+ Installer.new.uninstall(package, options[:d])
39
+ ui.puts "Successfully uninstalled #{package}"
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,128 @@
1
+ module Rip
2
+ module Env
3
+ extend self
4
+
5
+ def commands
6
+ instance_methods - %w( call active_dir commands ui )
7
+ end
8
+
9
+ def create(env)
10
+ dir = File.join(Rip.dir, env)
11
+
12
+ if env.strip.empty?
13
+ return "must give a ripenv to create"
14
+ end
15
+
16
+ if File.exists? dir
17
+ "#{env} exists"
18
+ else
19
+ FileUtils.mkdir_p File.join(dir, 'bin')
20
+ FileUtils.mkdir_p File.join(dir, 'lib')
21
+
22
+ use env
23
+ "created #{env}"
24
+ end
25
+ end
26
+
27
+ def use(env)
28
+ if env.strip.empty?
29
+ return "must give a ripenv to use"
30
+ end
31
+
32
+ if !File.exists?(target = File.join(Rip.dir, env))
33
+ return "#{env} doesn't exist"
34
+ end
35
+
36
+ begin
37
+ FileUtils.rm active_dir
38
+ rescue Errno::ENOENT
39
+ end
40
+ FileUtils.ln_s(target, active_dir)
41
+
42
+ "using #{env}"
43
+ end
44
+
45
+ def delete(env)
46
+ if active == env
47
+ return "can't delete active environment"
48
+ end
49
+
50
+ if env.strip.empty?
51
+ return "must give a ripenv to delete"
52
+ end
53
+
54
+ if File.exists?(target = File.join(Rip.dir, env))
55
+ FileUtils.rm_rf target
56
+ "deleted #{env}"
57
+ else
58
+ "can't find #{env}"
59
+ end
60
+ end
61
+
62
+ def list(env = nil)
63
+ envs = Dir.glob(File.join(Rip.dir, '*')).map do |env|
64
+ env.split('/').last
65
+ end
66
+
67
+ envs.reject! { |env| env =~ /^(rip-|active)/ }
68
+
69
+ if envs.empty?
70
+ "none. make one with `rip env create <env>`"
71
+ else
72
+ envs.join(' ')
73
+ end
74
+ end
75
+
76
+ def active
77
+ active = File.readlink(active_dir)
78
+ active.split('/').last
79
+ end
80
+
81
+ def copy(new)
82
+ if new.strip.empty?
83
+ return "must give a ripenv to copy to"
84
+ end
85
+
86
+ dest = File.join(Rip.dir, new)
87
+ src = Rip::Env.active_dir
88
+ env = Rip::Env.active
89
+
90
+ if File.exists?(dest)
91
+ return "#{new} exists"
92
+ end
93
+
94
+ FileUtils.cp_r src, dest
95
+
96
+ if File.exists? ripfile = File.join(dest, "#{env}.ripenv")
97
+ FileUtils.cp ripfile, File.join(dest, "#{new}.ripenv")
98
+ end
99
+
100
+ use new
101
+ "cloned #{env} to #{new}"
102
+ end
103
+
104
+ # for being lazy about what we have vs what we want.
105
+ # enables javascript-style method calling where
106
+ # the number of arguments doesn't need to match
107
+ # the arity
108
+ def call(meth, *args)
109
+ arity = method(meth).arity.abs
110
+
111
+ if arity == args.size
112
+ send(meth, *args)
113
+ elsif arity == 0
114
+ send(meth)
115
+ else
116
+ send(meth, args[0, arity])
117
+ end
118
+ end
119
+
120
+ def active_dir
121
+ File.join(Rip.dir, 'active')
122
+ end
123
+
124
+ def ui
125
+ Rip.ui
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,130 @@
1
+ module Rip
2
+ class Installer
3
+ include Memoize
4
+
5
+ attr_reader :installed
6
+ def initialize
7
+ @installed = {}
8
+ @uninstalled = {}
9
+ end
10
+
11
+ memoize :manager
12
+ def manager
13
+ PackageManager.new
14
+ end
15
+
16
+ def install(package, parent = nil)
17
+ if !package.exists?
18
+ error = package.name
19
+ error += " requested by #{parent} but" if parent
20
+ error += " not found at #{package.source}"
21
+ ui.abort error
22
+ end
23
+
24
+ Dir.chdir File.join(Rip.dir, 'rip-packages') do
25
+ begin
26
+ installed = @installed[package.name] || package.installed?
27
+
28
+ manager.add_package(package, parent) unless package.meta_package?
29
+
30
+ return if installed
31
+ @installed[package.name] = package
32
+
33
+ if !package.version
34
+ ui.abort "can't install #{package} - it has no version"
35
+ end
36
+
37
+ package.fetch
38
+ package.unpack
39
+ install_dependencies(package)
40
+ run_install_hook(package)
41
+ copy_files(package)
42
+ cleanup(package)
43
+ ui.puts "Successfully installed #{package}" unless package.meta_package?
44
+
45
+ rescue VersionConflict => e
46
+ ui.puts e.message
47
+ rollback
48
+ ui.abort "installation failed"
49
+
50
+ rescue => e
51
+ rollback
52
+ raise e
53
+ end
54
+ end
55
+ end
56
+
57
+ def install_dependencies(package)
58
+ package.dependencies.each do |dependency|
59
+ install(dependency, package)
60
+ end
61
+ end
62
+
63
+ def run_install_hook(package)
64
+ return unless File.exists? File.join(package.cache_path, 'Rakefile')
65
+
66
+ Dir.chdir package.cache_path do
67
+ ui.puts "running install hook for #{package.name}"
68
+ system "rake -s rip:install >& /dev/null"
69
+ end
70
+ end
71
+
72
+ def copy_files(package)
73
+ package_lib = File.join(package.cache_path, 'lib')
74
+ package_bin = File.join(package.cache_path, 'bin')
75
+
76
+ dest = Rip::Env.active_dir
77
+ dest_lib = File.join(dest, 'lib')
78
+ dest_bin = File.join(dest, 'bin')
79
+
80
+ if File.exists? package_lib
81
+ FileUtils.cp_r package_lib + '/.', dest_lib
82
+ end
83
+
84
+ if File.exists? package_bin
85
+ FileUtils.cp_r package_bin + '/.', dest_bin
86
+ end
87
+ end
88
+
89
+ def cleanup(package)
90
+ FileUtils.rm_rf package.cache_path unless package.cached?
91
+ end
92
+
93
+ def rollback
94
+ @installed.values.each do |package|
95
+ uninstall(package)
96
+ cleanup(package)
97
+ end
98
+ end
99
+
100
+ def uninstall(package, remove_dependencies = false)
101
+ packages = [package]
102
+
103
+ if remove_dependencies
104
+ packages.concat manager.packages_that_depend_on(package.name)
105
+ end
106
+
107
+ Dir.chdir Rip::Env.active_dir do
108
+ packages.each do |package|
109
+ begin
110
+ next if @uninstalled[package.name]
111
+ @uninstalled[package.name] = true
112
+
113
+ package.files.each do |file|
114
+ FileUtils.rm_rf file
115
+ end
116
+
117
+ manager.remove_package(package)
118
+ rescue => e
119
+ ui.puts e.message
120
+ next
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ def ui
127
+ Rip.ui
128
+ end
129
+ end
130
+ end