rip 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/README.markdown +18 -19
  2. data/lib/rip.rb +12 -6
  3. data/lib/rip/commands.rb +23 -8
  4. data/lib/rip/commands/build.rb +12 -3
  5. data/lib/rip/commands/core.rb +1 -3
  6. data/lib/rip/commands/install.rb +3 -1
  7. data/lib/rip/commands/ruby.rb +15 -0
  8. data/lib/rip/commands/setup.rb +21 -0
  9. data/lib/rip/commands/uninstall.rb +8 -1
  10. data/lib/rip/env.rb +18 -2
  11. data/lib/rip/installer.rb +13 -12
  12. data/lib/rip/package.rb +5 -1
  13. data/lib/rip/package_api.rb +8 -0
  14. data/lib/rip/package_manager.rb +21 -5
  15. data/lib/rip/packages/file_package.rb +7 -3
  16. data/lib/rip/packages/gem_package.rb +7 -3
  17. data/lib/rip/packages/git_package.rb +8 -3
  18. data/lib/rip/packages/http_package.rb +0 -1
  19. data/lib/rip/packages/remote_gem_package.rb +5 -1
  20. data/lib/rip/packages/ripfile_package.rb +23 -5
  21. data/lib/rip/setup.rb +120 -36
  22. data/lib/rip/sh/git.rb +6 -2
  23. data/lib/rip/version.rb +1 -1
  24. data/setup.rb +15 -1
  25. data/test/env_test.rb +23 -1
  26. data/test/file_test.rb +34 -0
  27. data/test/git_test.rb +20 -3
  28. data/test/mock_git.rb +7 -3
  29. data/test/repos/simple-file-3.2.1.rb +1 -0
  30. data/test/repos/simple.rip +2 -0
  31. data/test/repos/simple_c/dot_git/index +0 -0
  32. data/test/repos/simple_c/dot_git/logs/HEAD +1 -0
  33. data/test/repos/simple_c/dot_git/logs/refs/heads/master +1 -0
  34. data/test/repos/simple_c/dot_git/objects/4f/3f9a42a24970fb72e8a828d95652c08465b3a4 +0 -0
  35. data/test/repos/simple_c/dot_git/objects/64/38cb2cf06f0a0e96326f4223202bece66540a4 +0 -0
  36. data/test/repos/simple_c/dot_git/objects/9f/88aea8cd1b0da9b4d42188b7cf2014392344e6 +0 -0
  37. data/test/repos/simple_c/dot_git/objects/f1/050d684f583a32338ffd77ec37e1196e0d2cc7 +0 -0
  38. data/test/repos/simple_c/dot_git/refs/heads/master +1 -1
  39. data/test/ripfile_test.rb +19 -0
  40. data/test/test_helper.rb +35 -0
  41. metadata +19 -5
@@ -55,7 +55,7 @@ module Rip
55
55
  end
56
56
 
57
57
  def to_s
58
- "#{name} (#{version})"
58
+ version ? "#{name} (#{version})" : name.to_s
59
59
  end
60
60
 
61
61
  memoize :cache_name
@@ -119,6 +119,10 @@ module Rip
119
119
  end
120
120
  end
121
121
 
122
+ def run_hook(hook, *args, &block)
123
+ send(hook, *args, &block) if respond_to? hook
124
+ end
125
+
122
126
  def ui
123
127
  Rip.ui
124
128
  end
@@ -34,6 +34,14 @@
34
34
  # end
35
35
  # end
36
36
  #
37
+ # Hooks are provided to give your package access to the installation
38
+ # process. If your package implements a method with the same name as
39
+ # a hook, it'll be called and passed relevant information.
40
+ #
41
+ # The following hooks currently exist:
42
+ #
43
+ # dependency_installed(dependency<Package>, success<Boolean>)
44
+ #
37
45
 
38
46
  module Rip
39
47
  module PackageAPI
@@ -2,6 +2,9 @@ require 'zlib'
2
2
  require 'set'
3
3
 
4
4
  module Rip
5
+ class CorruptedRipenv < RuntimeError
6
+ end
7
+
5
8
  class VersionConflict < RuntimeError
6
9
  def initialize(name, bad_version, requester, real_version, owners)
7
10
  @name = name
@@ -14,17 +17,23 @@ module Rip
14
17
  def message
15
18
  message = []
16
19
  message << "version conflict!"
17
- message << "#{@name} requested at #{@bad_version} by #{@requester}"
20
+
21
+ requested = "#{@name} requested at #{@bad_version}"
22
+ requested += " by #{@requester}" if @requester
23
+ message << requested
18
24
 
19
25
  if @owners.size == 1
20
26
  owners = @owners[0]
21
27
  elsif @owners.size == 2
22
28
  owners = "#{@owners[0]} and #{@owners[1]}"
23
- else
29
+ elsif @owners.size > 2
24
30
  owners = [ @owners[0...-1], "and #{@owners[-1]}" ].join(', ')
25
31
  end
26
32
 
27
- message << "#{@name} previously requested at #{@real_version} by #{owners}"
33
+ previously_requested = "#{@name} previously requested at #{@real_version}"
34
+ previously_requested += " by #{owners}" if owners
35
+ message << previously_requested
36
+
28
37
  message.join("\n")
29
38
  end
30
39
  alias_method :to_s, :message
@@ -69,9 +78,14 @@ module Rip
69
78
  @versions.keys
70
79
  end
71
80
 
81
+ # If given a package name, finds and returns the package.
82
+ # If given a package, returns it.
72
83
  def package(name)
73
- return unless @versions[name]
74
- Package.for(@sources[name], @versions[name], @files[name])
84
+ if name.respond_to? :cache_path
85
+ name
86
+ elsif @versions[name]
87
+ Package.for(@sources[name], @versions[name], @files[name])
88
+ end
75
89
  end
76
90
 
77
91
  def packages_that_depend_on(name)
@@ -162,6 +176,8 @@ module Rip
162
176
 
163
177
  def unzip(data)
164
178
  Zlib::Inflate.inflate(data)
179
+ rescue Zlib::BufError
180
+ raise CorruptedRipenv.new("#{path} possibly corrupted")
165
181
  end
166
182
 
167
183
  def marshal_payload
@@ -17,17 +17,21 @@ module Rip
17
17
 
18
18
  memoize :name
19
19
  def name
20
- source.split('/').last
20
+ File.basename(source)
21
21
  end
22
22
 
23
23
  def version
24
- Date.today.to_s
24
+ if name.match(/-((?:\d+\.?)+\d+)\.rb$/)
25
+ $1
26
+ else
27
+ Date.today.to_s
28
+ end
25
29
  end
26
30
 
27
31
  def fetch!
28
32
  FileUtils.rm_rf cache_path
29
33
  FileUtils.mkdir_p cache_path
30
- FileUtils.cp source, cache_path
34
+ FileUtils.cp source, File.join(cache_path, name)
31
35
  end
32
36
 
33
37
  def files!
@@ -20,8 +20,8 @@ module Rip
20
20
  end
21
21
 
22
22
  def exists?
23
- if `which gem`.strip.empty?
24
- ui.abort "you don't have rubygems installed"
23
+ if `which #{gembin}`.strip.empty?
24
+ ui.abort "you don't have #{gembin} installed"
25
25
  end
26
26
 
27
27
  File.exists?(source)
@@ -32,7 +32,7 @@ module Rip
32
32
  end
33
33
 
34
34
  def unpack!
35
- system "gem unpack #{cache_file} --target=#{packages_path} > /dev/null"
35
+ system "'#{gembin}' unpack '#{cache_file}' --target='#{packages_path}' > /dev/null"
36
36
  end
37
37
 
38
38
  memoize :metadata
@@ -40,5 +40,9 @@ module Rip
40
40
  parts = source.split('/').last.chomp('.gem').split('-')
41
41
  { :name => parts[0...-1].join('-'), :version => parts[-1] }
42
42
  end
43
+
44
+ def gembin
45
+ ENV['GEMBIN'] || 'gem'
46
+ end
43
47
  end
44
48
  end
@@ -14,7 +14,7 @@ module Rip
14
14
 
15
15
  fetch!
16
16
  Dir.chdir cache_path do
17
- @version = git_revparse('origin/master')[0,7]
17
+ @version = git_rev_parse('origin/master')[0,7]
18
18
  end
19
19
  end
20
20
 
@@ -55,8 +55,13 @@ module Rip
55
55
  end
56
56
 
57
57
  def remote_exists?
58
- out = git_ls_remote(source, @version)
59
- out.include? @version || 'HEAD'
58
+ return false if git_ls_remote(source).size == 0
59
+ return true if !@version
60
+
61
+ fetch
62
+ Dir.chdir(cache_path) do
63
+ git_cat_file(@version).size > 0
64
+ end
60
65
  end
61
66
  end
62
67
  end
@@ -38,7 +38,6 @@ module Rip
38
38
  actual_package ? actual_package.version : super
39
39
  end
40
40
 
41
- memoize :actual_package
42
41
  def actual_package
43
42
  Package.for(File.join(cache_path, name))
44
43
  end
@@ -32,7 +32,7 @@ module Rip
32
32
 
33
33
  def rgem(command)
34
34
  Timeout.timeout(5) do
35
- `gem #{command}`
35
+ `#{gembin} #{command}`
36
36
  end
37
37
  rescue Timeout::Error
38
38
  ''
@@ -60,5 +60,9 @@ module Rip
60
60
  def actual_package
61
61
  Package.for(Dir[cache_path + '/*'].first)
62
62
  end
63
+
64
+ def gembin
65
+ ENV['GEMBIN'] || 'gem'
66
+ end
63
67
  end
64
68
  end
@@ -15,6 +15,10 @@ module Rip
15
15
  source.split('/').last
16
16
  end
17
17
 
18
+ def version
19
+ nil
20
+ end
21
+
18
22
  def meta_package?
19
23
  true
20
24
  end
@@ -23,20 +27,34 @@ module Rip
23
27
  false
24
28
  end
25
29
 
26
- def fetch!
30
+ def dependency_installed(dependency, success = true)
31
+ if !success
32
+ ui.puts "rip: already installed #{dependency}"
33
+ end
27
34
  end
28
35
 
29
- def unpack!
36
+ def fetch!
30
37
  FileUtils.rm_rf cache_path
31
38
  FileUtils.mkdir_p cache_path
32
- FileUtils.cp source, cache_path
39
+ FileUtils.cp source, File.join(cache_path, name)
40
+ end
41
+
42
+ def unpack!
43
+ fetch
33
44
  end
34
45
 
35
46
  def dependencies!
36
47
  if File.exists? deps = File.join(cache_path, name)
37
48
  File.readlines(deps).map do |line|
38
- source, version, *extra = line.split(' ')
39
- Package.for(source, version)
49
+ package_source, version, *extra = line.split(' ')
50
+ if package = Package.for(package_source, version)
51
+ package
52
+ else
53
+ # Allows .rip file and dir packages to be listed as
54
+ # relative paths.
55
+ path = File.join(File.dirname(@source), package_source)
56
+ Package.for(path, version)
57
+ end
40
58
  end
41
59
  else
42
60
  []
@@ -11,20 +11,38 @@ module Rip
11
11
  #
12
12
 
13
13
  WEBSITE = "http://hellorip.com/"
14
- STARTUP_SCRIPTS = %w( .bash_profile .bash_login .bashrc .zshrc .profile )
14
+ STARTUP_SCRIPTS = %w( .bash_profile .bash_login .bashrc .zshenv .profile .zshrc )
15
+ FISH_STARTUP_SCRIPT = ".config/fish/config.fish"
15
16
 
16
17
  __DIR__ = File.expand_path(File.dirname(__FILE__))
17
18
 
18
19
  HOME = File.expand_path('~')
19
- USER = HOME.split('/')[-1]
20
+
21
+ USER = HOME.split('/')[-1] # TODO: *cough*
22
+
23
+ # Work around Apple's Ruby.
24
+ #
25
+ BINDIR = if defined? RUBY_FRAMEWORK_VERSION
26
+ File.join("/", "usr", "bin")
27
+ else
28
+ RbConfig::CONFIG["bindir"]
29
+ end
30
+
20
31
  LIBDIR = RbConfig::CONFIG['sitelibdir']
32
+
21
33
  RIPDIR = File.expand_path(ENV['RIPDIR'] || File.join(HOME, '.rip'))
22
34
  RIPROOT = File.expand_path(File.join(__DIR__, '..', '..'))
23
35
  RIPINSTALLDIR = File.join(LIBDIR, 'rip')
24
36
 
25
- # caution: RbConfig::CONFIG['bindir'] does NOT work for me
26
- # on OS X
27
- BINDIR = File.join('/', 'usr', 'local', 'bin')
37
+
38
+ # Indicates that Rip isn't properly installed.
39
+ class InstallationError < StandardError; end
40
+
41
+ # Indicates that Rip is properly installed, but the current shell
42
+ # hasn't picked up the installed Rip environment variables yet. The
43
+ # shell must be restarted for the changes to become effective, or
44
+ # the shell startup files must be re-sourced.
45
+ class StaleEnvironmentError < StandardError; end
28
46
 
29
47
 
30
48
  #
@@ -39,24 +57,35 @@ module Rip
39
57
  finish_setup
40
58
  end
41
59
 
60
+ def upgrade
61
+ remove_libs
62
+ install_libs
63
+ ui.puts "rip upgraded"
64
+ end
65
+
42
66
  def uninstall(verbose = false)
43
- FileUtils.rm_rf RIPINSTALLDIR, :verbose => verbose
44
- FileUtils.rm_rf File.join(LIBDIR, 'rip.rb'), :verbose => verbose
45
- FileUtils.rm_rf RIPDIR, :verbose => verbose
46
67
  FileUtils.rm File.join(BINDIR, 'rip'), :verbose => verbose
68
+ remove_libs verbose
69
+ FileUtils.rm_rf RIPDIR, :verbose => verbose
47
70
 
48
71
  # just in case...
49
- `gem uninstall rip 2&> /dev/null`
72
+ gembin = ENV['GEMBIN'] || 'gem'
73
+ `#{gembin} uninstall rip 2&> /dev/null`
50
74
 
51
75
  ui.abort "rip uninstalled" if verbose
52
76
  rescue Errno::EACCES
53
- ui.abort "rip: uninstall failed. please try again with `sudo`" if verbose
77
+ ui.abort "uninstall failed. please try again with `sudo`" if verbose
54
78
  rescue Errno::ENOENT
55
79
  nil
56
80
  rescue => e
57
81
  raise e if verbose
58
82
  end
59
83
 
84
+ def remove_libs(verbose = false)
85
+ FileUtils.rm_rf RIPINSTALLDIR, :verbose => verbose
86
+ FileUtils.rm_rf File.join(LIBDIR, 'rip.rb'), :verbose => verbose
87
+ end
88
+
60
89
  def install_libs(verbose = false)
61
90
  transaction "installing library files" do
62
91
  riprb = File.join(RIPROOT, 'lib', 'rip.rb')
@@ -70,7 +99,13 @@ module Rip
70
99
  transaction "installing rip binary" do
71
100
  src = File.join(RIPROOT, 'bin', 'rip')
72
101
  dst = File.join(BINDIR, 'rip')
73
- FileUtils.cp src, dst, :verbose => verbose
102
+ FileUtils.cp src, dst, :verbose => verbose, :preserve => true
103
+
104
+ ruby_bin = File.expand_path(File.join(BINDIR, RbConfig::CONFIG['ruby_install_name']))
105
+ if File.exist? ruby_bin
106
+ ui.puts "rip: using Ruby bin: #{ruby_bin}"
107
+ rewrite_bang_line(dst, "#!#{ruby_bin}")
108
+ end
74
109
  end
75
110
  end
76
111
 
@@ -83,22 +118,37 @@ module Rip
83
118
  end
84
119
  end
85
120
 
86
- def setup_startup_script
87
- script = startup_script
121
+ # Modifies the shell startup script(s) and inserts the Rip
122
+ # configuration statements.
123
+ #
124
+ # Returns whether a startup script has been modified. If one of
125
+ # the startup scripts already contain the Rip configuration
126
+ # statements, then nothing will be modified and false will be
127
+ # returned.
128
+ #
129
+ # TODO: Requires the startup script, but probably acceptable for most? --rue
130
+ #
131
+ def setup_startup_script(script = nil)
132
+ if script
133
+ script = File.expand_path(script)
134
+ else
135
+ script = startup_script
136
+ end
88
137
 
89
- if script.empty?
90
- ui.puts "rip: please create one of these startup scripts in $HOME:"
91
- ui.puts startup_scripts.map { |s| ' ' + s }
92
- exit
138
+ if script.empty? || !File.exists?(script)
139
+ ui.puts "rip: please create one of these startup scripts in $HOME and re-run:"
140
+ ui.abort STARTUP_SCRIPTS.map { |s| ' ' + s }
93
141
  end
94
142
 
95
- if File.read(script).include? 'RIPDIR='
143
+ if File.read(script).include? 'RIPDIR'
96
144
  ui.puts "rip: env variables already present in startup script"
145
+ false
97
146
  else
98
147
  ui.puts "rip: adding env variables to #{script}"
99
148
  File.open(script, 'a+') do |f|
100
149
  f.puts startup_script_template
101
150
  end
151
+ true
102
152
  end
103
153
  end
104
154
 
@@ -106,15 +156,16 @@ module Rip
106
156
  ui.puts finish_setup_banner(startup_script)
107
157
  end
108
158
 
109
- def finish_setup_banner(script = "~/.bashrc")
159
+ def finish_setup_banner(script = "~/.bash_profile")
110
160
  <<-EOI.gsub(/^ +/, "")
111
161
  ****************************************************
112
162
  So far so good...
113
163
 
114
- Run `rip check` to be sure Rip installed successfully
164
+ Rip needs certain env variables to run. We've tried
165
+ to install them automatically but may have failed.
115
166
 
116
- NOTE: You may need to source your #{script}
117
- or start a new shell session.
167
+ Run `rip check` to check the status of your
168
+ installation.
118
169
 
119
170
  Get started: `rip -h` or #{WEBSITE}
120
171
 
@@ -139,17 +190,30 @@ module Rip
139
190
  end
140
191
 
141
192
  def startup_script_template
142
- STARTUP_SCRIPT_TEMPLATE % RIPDIR
193
+ (fish? ? FISH_CONFIG_TEMPLATE : STARTUP_SCRIPT_TEMPLATE) % RIPDIR
143
194
  end
144
195
 
145
196
  def startup_script
146
- script = STARTUP_SCRIPTS.detect do |script|
197
+ script = fish_startup_script || STARTUP_SCRIPTS.detect do |script|
147
198
  File.exists? file = File.join(HOME, script)
148
199
  end
149
200
 
150
201
  script ? File.join(HOME, script) : ''
151
202
  end
152
203
 
204
+ def startup_script_contains_rip_configuration?
205
+ filename = startup_script
206
+ !filename.empty? && File.read(filename).include?(startup_script_template)
207
+ end
208
+
209
+ def fish_startup_script
210
+ FISH_STARTUP_SCRIPT if fish?
211
+ end
212
+
213
+ def fish?
214
+ File.exists?(File.join(HOME, FISH_STARTUP_SCRIPT))
215
+ end
216
+
153
217
  def installed?
154
218
  check_installation
155
219
  true
@@ -158,35 +222,47 @@ module Rip
158
222
  end
159
223
 
160
224
  def check_installation
161
- script = startup_script
162
-
163
- if !File.read(script).include? 'RIPDIR='
164
- raise "no env variables in startup script"
165
- end
166
-
167
225
  if ENV['RIPDIR'].to_s.empty?
168
- if startup_script.empty?
169
- raise "no $RIPDIR."
226
+ if startup_script_contains_rip_configuration?
227
+ raise StaleEnvironmentError, <<-end_error
228
+ No $RIPDIR. Rip has been integrated into your shell startup scripts but your
229
+ shell hasn't yet picked up the changes.
230
+
231
+ To complete the installation process please restart your shell or run:
232
+ source #{startup_script}
233
+ end_error
170
234
  else
171
- raise "no $RIPDIR. you may need to run `source #{startup_script}`"
235
+ raise InstallationError, <<-end_error
236
+ No $RIPDIR. Rip hasn't been integrated into your shell startup scripts yet.
237
+ Please run `rip setup` to do so.
238
+ end_error
172
239
  end
173
240
  end
174
241
 
175
242
  if !File.exists? File.join(BINDIR, 'rip')
176
- raise "no rip in #{BINDIR}"
243
+ raise InstallationError, "no rip in #{BINDIR}"
177
244
  end
178
245
 
179
246
  if !File.exists? File.join(LIBDIR, 'rip')
180
- raise "no rip in #{LIBDIR}"
247
+ raise InstallationError, "no rip in #{LIBDIR}"
181
248
  end
182
249
 
183
250
  if !File.exists? File.join(LIBDIR, 'rip')
184
- raise "no rip.rb in #{LIBDIR}"
251
+ raise InstallationError, "no rip.rb in #{LIBDIR}"
185
252
  end
186
253
 
187
254
  true
188
255
  end
189
256
 
257
+ def rewrite_bang_line(file, first_line)
258
+ lines = File.readlines(file)[1..-1]
259
+ File.open(file, 'w') do |f|
260
+ f.puts first_line
261
+ f.puts lines.join("\n")
262
+ f.flush
263
+ end
264
+ end
265
+
190
266
  def ui
191
267
  Rip.ui
192
268
  end
@@ -200,6 +276,14 @@ RUBYLIB="$RUBYLIB:$RIPDIR/active/lib"
200
276
  PATH="$PATH:$RIPDIR/active/bin"
201
277
  export RIPDIR RUBYLIB PATH
202
278
  # -- end rip config -- #
279
+ end_template
280
+
281
+ FISH_CONFIG_TEMPLATE = <<-end_template
282
+ # -- start rip config -- #
283
+ set -x RIPDIR %s
284
+ set -x RUBYLIB "$RUBYLIB:$RIPDIR/active/lib"
285
+ set PATH $RIPDIR/active/bin $PATH
286
+ # -- end rip config -- #
203
287
  end_template
204
288
 
205
289
  end