rip 0.0.1 → 0.0.2

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