sundae 0.9.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/{History.txt → CHANGELOG} +8 -0
- data/README.rdoc +155 -0
- data/Rakefile +27 -15
- data/bin/sundae +94 -21
- data/lib/sundae.rb +111 -49
- data/test/test_sundae.rb +17 -38
- data/version.txt +1 -0
- metadata +96 -65
- data/Manifest.txt +0 -9
- data/README.txt +0 -108
- data/setup.rb +0 -1599
- /data/{Copying.txt → COPYING} +0 -0
data/.gitignore
ADDED
data/{History.txt → CHANGELOG}
RENAMED
@@ -1,3 +1,11 @@
|
|
1
|
+
=== 1.0.0 / 2012-02-21
|
2
|
+
|
3
|
+
* Backwards incompatible: use Ruby for config file (not YAML)
|
4
|
+
* minor enhancements:
|
5
|
+
* support globs or regexp for ignore_paths
|
6
|
+
* better documentation
|
7
|
+
* switched to bones for gem config
|
8
|
+
|
1
9
|
=== 0.9.2 / 2009-04-28
|
2
10
|
|
3
11
|
* 1 minor enhancement
|
data/README.rdoc
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
= Sundae
|
2
|
+
|
3
|
+
== Synopsis
|
4
|
+
|
5
|
+
(Re)generates directories by mixing the file hierarchies contained in
|
6
|
+
various 'mounted' directories. The generated directories contain
|
7
|
+
symbolic links to the mounted files. Combined with other tools (to
|
8
|
+
sync files), this scheme allows you to create separate collections of
|
9
|
+
files (work, personal, reference, linux, osx, etc.), choose which of
|
10
|
+
these you want to mount on each of your computers, and then build a
|
11
|
+
hierarchy that allows you to work on them side by side.
|
12
|
+
|
13
|
+
For example, let's take your bash config files. You want to separate
|
14
|
+
startup commands that you use on all unix computers from those that
|
15
|
+
you only need on Linux or OS X. Plus maybe you have some aliases that
|
16
|
+
you use only at work and some that you need only at home.
|
17
|
+
|
18
|
+
Rewrite your .bashrc to load everything in ~/etc/bash. Store that
|
19
|
+
file and your other bash config files in a folder with just general
|
20
|
+
*nix bash stuff. Then use Sundae to create links in ~/etc/bash to all
|
21
|
+
of the things that you need for your particular computer (general unix
|
22
|
+
stuff + either linux or OS X stuff + work aliases + ...). The files
|
23
|
+
are together in one folder so your script knows to read them, but they
|
24
|
+
can be version controlled and/or synced across all of your computers
|
25
|
+
in separate bundles (one for *nix, one for ubuntu, one for OS X,
|
26
|
+
etc.).
|
27
|
+
|
28
|
+
You have to figure out how to use this model. It's not for everyone.
|
29
|
+
It requires rewriting config files, making sure certain files kept in
|
30
|
+
different folders don't have the same name, etc. But it's worth it
|
31
|
+
when you sit down at a new computer and say "I only want to use my
|
32
|
+
*nix and linux config files, my work files, and music" and it all just
|
33
|
+
works. Like this:
|
34
|
+
|
35
|
+
~> ls
|
36
|
+
Desktop local mnt src WualaDrive
|
37
|
+
~> sundae
|
38
|
+
~> ls
|
39
|
+
bin Desktop doc etc lib local mnt share src tmp var WualaDrive
|
40
|
+
|
41
|
+
== Install
|
42
|
+
|
43
|
+
sudo gem install sundae
|
44
|
+
|
45
|
+
== Usage
|
46
|
+
|
47
|
+
The first time you run Sundae, it will create a template config file
|
48
|
+
in your home directory. This file, <tt>.sundae</tt>, needs to be
|
49
|
+
customized. It is just a Ruby file that defines the following:
|
50
|
+
|
51
|
+
[+configatron.paths+]
|
52
|
+
array; where the collections are stored
|
53
|
+
[+configatron.ignore_rules+]
|
54
|
+
array; each element is a string or Regexp and becomes a rule that prevents
|
55
|
+
links to files or directories that match the Regexp. Globs in strings are expanded.
|
56
|
+
|
57
|
+
The hierarchy in <em>path</em> should look something like
|
58
|
+
this:
|
59
|
+
|
60
|
+
path/
|
61
|
+
|-- collection1/
|
62
|
+
| |-- mnt1/
|
63
|
+
| | |-- real_files_and_dirs
|
64
|
+
| | ` ...
|
65
|
+
| |-- mnt2/
|
66
|
+
`-- collection2/
|
67
|
+
` ...
|
68
|
+
|
69
|
+
Why is this double layer "collection" stuff going on? Because while
|
70
|
+
most of the time you can share a whole folder between computers,
|
71
|
+
sometimes you want to mix your config files into a folder that also
|
72
|
+
contains nonsymlinked files.
|
73
|
+
|
74
|
+
For example, your ~/.ssh folder probably has a public and private key
|
75
|
+
that you want to stay unique to that machine, but you might want to
|
76
|
+
mix in a "config" file that has host aliases that you share between
|
77
|
+
machines. This is how you do that. I do it with my .unison, .mocp,
|
78
|
+
.ssh, and .lftp folders.
|
79
|
+
|
80
|
+
For example, the hierarchy in my <em>path</em>s looks sort of like this:
|
81
|
+
|
82
|
+
~/mnt/git/ <-- "path"
|
83
|
+
|-- nix/ <-- "collection"
|
84
|
+
| |-- home/ <-- "mnt"
|
85
|
+
| | |-- .emacs.d/ (~/.emacs.d will point here)
|
86
|
+
| | |-- etc/ (~/etc will point here)
|
87
|
+
| | ` ...
|
88
|
+
| |-- dot-unison
|
89
|
+
| | |-- .sundae_path (says "~/.unison")
|
90
|
+
| | |-- default.prf (~/.unison/default.prf will point here)
|
91
|
+
| | `
|
92
|
+
| |
|
93
|
+
|-- osx/
|
94
|
+
| |-- home_library/ (says "~/Library")
|
95
|
+
| | |-- .sundae_path
|
96
|
+
| | `-- Library-Keyboard_Layouts/
|
97
|
+
| | `-- Keyboard Layouts/
|
98
|
+
| | ` Colemak.keylayout
|
99
|
+
| |
|
100
|
+
|-- personal
|
101
|
+
| `-- home/
|
102
|
+
| |-- doc/
|
103
|
+
| | ` ...
|
104
|
+
| ` ...
|
105
|
+
` ...
|
106
|
+
~/mnt/sync/ <-- "path"
|
107
|
+
|-- reference <-- "collection"
|
108
|
+
| |-- home/ <-- "mnt"
|
109
|
+
| ` ...
|
110
|
+
|-- music
|
111
|
+
| |-- home/
|
112
|
+
| ` ...
|
113
|
+
` ...
|
114
|
+
|
115
|
+
Sundae will act on all of the <em>mnt</em>s--subdirectories of the
|
116
|
+
<em>collection</em>s, that is, the sub-subdirectories of the
|
117
|
+
<em>path</em>. The "collections" are only there to facilitate
|
118
|
+
grouping common files and syncronizing them between computers.
|
119
|
+
|
120
|
+
By default, all of the contents in each of the <em>mnt</em>s are
|
121
|
+
placed in the user's home directory. This can be altered by
|
122
|
+
creating a file called <tt>.sundae_path</tt> in the top of the
|
123
|
+
<em>mnt</em>; the file should contain one line, which is the
|
124
|
+
absolute path to where that directory should be "mounted."
|
125
|
+
|
126
|
+
And that's it. When called, Sundae creates links so that you can
|
127
|
+
work on your files from seperate parts of life as if they were side
|
128
|
+
by side.
|
129
|
+
|
130
|
+
== Author
|
131
|
+
<don@ohspite.net>
|
132
|
+
|
133
|
+
== Copyright
|
134
|
+
Copyright (c) 2011, 2008 <don@ohspite.net>.
|
135
|
+
Licensed under the MIT License.
|
136
|
+
|
137
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
138
|
+
a copy of this software and associated documentation files (the
|
139
|
+
'Software'), to deal in the Software without restriction, including
|
140
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
141
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
142
|
+
permit persons to whom the Software is furnished to do so, subject to
|
143
|
+
the following conditions:
|
144
|
+
|
145
|
+
The above copyright notice and this permission notice shall be
|
146
|
+
included in all copies or substantial portions of the Software.
|
147
|
+
|
148
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
149
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
150
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
151
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
152
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
153
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
154
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
155
|
+
|
data/Rakefile
CHANGED
@@ -1,18 +1,30 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'bones'
|
3
|
+
rescue LoadError
|
4
|
+
abort '### please install the "bones" gem ###'
|
5
|
+
end
|
2
6
|
|
3
|
-
|
4
|
-
|
5
|
-
require './lib/sundae.rb'
|
7
|
+
task :default => 'test:run'
|
8
|
+
task 'gem:release' => 'test:run'
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
Bones do
|
11
|
+
name 'sundae'
|
12
|
+
authors 'Don'
|
13
|
+
email 'don@ohspite.net'
|
14
|
+
url 'https://github.com/ohspite/sundae'
|
15
|
+
summary 'Mix collections of files while maintaining complete separation.'
|
16
|
+
description 'Mix collections of files while maintaining complete separation.'
|
17
|
+
history_file 'CHANGELOG'
|
18
|
+
manifest_file 'Manifest'
|
19
|
+
readme_file 'README.rdoc'
|
20
|
+
rdoc.main 'README.rdoc'
|
17
21
|
|
18
|
-
|
22
|
+
ignore_file '.gitignore'
|
23
|
+
exclude %w(tmp$ bak$ ~$ CVS \.svn/ \.git/ \.bzr/ \.bzrignore ^pkg/)
|
24
|
+
rdoc.include %w(README ^lib/ ^bin/ ^ext/ \.txt$ \.rdoc$)
|
25
|
+
depend_on 'highline'
|
26
|
+
depend_on 'configatron'
|
27
|
+
depend_on 'rdoc'
|
28
|
+
|
29
|
+
# spec.opts << '--color'
|
30
|
+
end
|
data/bin/sundae
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
#
|
8
8
|
# == Usage
|
9
9
|
#
|
10
|
-
# sundae [
|
10
|
+
# sundae [options] [commands]
|
11
11
|
#
|
12
12
|
# For command line details see
|
13
13
|
# sundae --help
|
@@ -16,25 +16,84 @@
|
|
16
16
|
# <don@ohspite.net>
|
17
17
|
#
|
18
18
|
# == Copyright
|
19
|
-
# Copyright (c) 2008 <don@ohspite.net>.
|
19
|
+
# Copyright (c) 2012, 2008 <don@ohspite.net>.
|
20
20
|
# Licensed under the MIT License.
|
21
21
|
|
22
|
-
require 'rdoc/usage'
|
23
22
|
require 'optparse'
|
23
|
+
require 'highline/import'
|
24
24
|
|
25
25
|
$:.unshift File.join(File.dirname(__FILE__), "../lib")
|
26
26
|
|
27
27
|
require 'sundae'
|
28
28
|
|
29
29
|
class App # :nodoc:
|
30
|
+
|
31
|
+
COMMAND_LIST = %w{run
|
32
|
+
source
|
33
|
+
move
|
34
|
+
}
|
35
|
+
|
30
36
|
def initialize
|
31
37
|
parse_commandline(ARGV)
|
32
38
|
|
33
39
|
Sundae.load_config_file(@options[:config_path])
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
40
|
+
|
41
|
+
case @commands[0]
|
42
|
+
when :run
|
43
|
+
Sundae.update_filesystem
|
44
|
+
when :remove
|
45
|
+
Sundae.remove_filesystem
|
46
|
+
when :sources
|
47
|
+
ARGV << "." if ARGV.empty?
|
48
|
+
Process.abort "#{ARGV[0]} is not a directory." unless File.directory?(ARGV[0])
|
49
|
+
path = File.expand_path(ARGV[0])
|
50
|
+
mnts = Sundae.find_source_directories(path)
|
51
|
+
mnts.each do |mnt|
|
52
|
+
install_location = Sundae.install_location(mnt)
|
53
|
+
relative = path.sub(install_location, '')
|
54
|
+
puts mnt
|
55
|
+
Dir.entries(File.join(mnt, relative)).sort.each do |e|
|
56
|
+
next if e =~ /^..?$/
|
57
|
+
line_end = File.directory?(e) ? "/" : ""
|
58
|
+
puts " " + e + line_end
|
59
|
+
end
|
60
|
+
puts
|
61
|
+
end
|
62
|
+
when :move
|
63
|
+
Process.abort "The 'move' command requires a file or directory as an argument." if ARGV.empty?
|
64
|
+
Process.abort "#{ARGV[0]} is not a file or directory." unless File.exist?(ARGV[0])
|
65
|
+
|
66
|
+
if ARGV.size == 1
|
67
|
+
path = File.expand_path(ARGV[0])
|
68
|
+
|
69
|
+
current_mnt = nil
|
70
|
+
Sundae.all_mnts.map do |mnt|
|
71
|
+
current_mnt = mnt if path =~ Regexp.new(mnt)
|
72
|
+
end
|
73
|
+
choices = if current_mnt
|
74
|
+
Sundae.all_mnts
|
75
|
+
else
|
76
|
+
Sundae.find_source_directories(File.dirname(path))
|
77
|
+
end
|
78
|
+
choices.push "--Cancel?--"
|
79
|
+
choose do |menu|
|
80
|
+
menu.prompt = "move to: "
|
81
|
+
menu.choices(*choices) do |new_path|
|
82
|
+
if new_path == "--Cancel?--"
|
83
|
+
Process.abort("No move performed.")
|
84
|
+
else
|
85
|
+
Sundae.move_to_mnt(path, new_path)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
else
|
90
|
+
relative_path = ARGV.pop
|
91
|
+
ARGV.each do |path|
|
92
|
+
path = File.expand_path(path)
|
93
|
+
Sundae.move_to_relative_path(path, relative_path)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
38
97
|
end
|
39
98
|
|
40
99
|
private
|
@@ -42,12 +101,18 @@ class App # :nodoc:
|
|
42
101
|
def parse_commandline(option_line)
|
43
102
|
options = {:verbose => false}
|
44
103
|
option_parser = OptionParser.new do |opts|
|
45
|
-
opts.banner = "Usage: #{File.basename(__FILE__)} [options] "
|
104
|
+
opts.banner = "Usage: #{File.basename(__FILE__)} [options] [command]"
|
105
|
+
opts.separator ""
|
106
|
+
opts.separator "Available commands (can be abbreviated):"
|
107
|
+
opts.separator ' run create the filesystem from the mounts; the default command'
|
108
|
+
opts.separator ' rm remove generated directories and symlinks'
|
109
|
+
opts.separator ' move PATH moves the resource at \'path\' (or pointed to by \'path\' if it is a link) to another mount collection'
|
110
|
+
opts.separator ' sources DIR=./ print the mounts that have resources in \'dir\' and what comes from each'
|
46
111
|
opts.separator ""
|
47
112
|
opts.separator "Specific options:"
|
48
113
|
opts.on('-c',
|
49
114
|
'--config-path PATH',
|
50
|
-
'specify the path to the \'.sundae\' directory (default is \'~/.sundae\')') do |path|
|
115
|
+
'specify the path to the \'.sundae\' directory (default is \'~/.sundae\'); used with the regular \'run\' command') do |path|
|
51
116
|
options[:config_path] = File.expand_path(path)
|
52
117
|
end
|
53
118
|
# opts.on('-v',
|
@@ -71,23 +136,31 @@ class App # :nodoc:
|
|
71
136
|
end
|
72
137
|
|
73
138
|
argv = Array.new
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
139
|
+
commands = Array.new
|
140
|
+
if option_line.empty? then option_line = ['run'] end
|
141
|
+
option_parser.order!(option_line) do |input|
|
142
|
+
action = case input
|
143
|
+
when nil then :run
|
144
|
+
when /^ru?n?$/ then :run
|
145
|
+
when /^re?m?o?v?e?$/ then :remove
|
146
|
+
when /^so?u?r?c?e?s?$/ then :sources
|
147
|
+
when /^mo?v?e?$/ then :move
|
148
|
+
else nil
|
149
|
+
end
|
150
|
+
if action.nil?
|
151
|
+
argv << input
|
152
|
+
else
|
153
|
+
commands << action
|
82
154
|
end
|
83
|
-
rescue
|
84
|
-
RDoc::usage('usage')
|
85
155
|
end
|
86
156
|
argv.each { |a| option_line << a }
|
87
|
-
|
157
|
+
|
158
|
+
if commands.empty? then Process.abort "No command unambiguously specified." end
|
159
|
+
if commands.size > 1 then Process.abort "More than one command specified." end
|
160
|
+
|
88
161
|
@options = options
|
162
|
+
@commands = commands
|
89
163
|
end
|
90
164
|
end
|
91
165
|
|
92
|
-
|
93
166
|
App.new
|
data/lib/sundae.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'rubygems'
|
1
|
+
# require 'rubygems'
|
2
2
|
require 'configatron'
|
3
3
|
require 'fileutils'
|
4
4
|
require 'find'
|
@@ -7,7 +7,11 @@ require 'find'
|
|
7
7
|
# together using symbolic links.
|
8
8
|
#
|
9
9
|
module Sundae
|
10
|
-
|
10
|
+
# :stopdoc:
|
11
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
12
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
13
|
+
# :startdoc:
|
14
|
+
VERSION = ::File.read(PATH + 'version.txt').strip
|
11
15
|
|
12
16
|
DEFAULT_CONFIG_FILE = File.expand_path(File.join(ENV['HOME'], '.sundae'))
|
13
17
|
|
@@ -17,25 +21,18 @@ module Sundae
|
|
17
21
|
#
|
18
22
|
def self.load_config_file(config_file = DEFAULT_CONFIG_FILE)
|
19
23
|
config_file ||= DEFAULT_CONFIG_FILE
|
20
|
-
config_file = File.join(config_file, '.sundae')
|
24
|
+
config_file = File.join(config_file, '.sundae') if File.directory?(config_file)
|
21
25
|
|
22
26
|
create_template_config_file(config_file) unless File.file?(config_file)
|
23
27
|
|
24
|
-
|
25
|
-
configatron.set_default(:collection_link_prefix, '_')
|
26
|
-
|
27
|
-
configatron.configure_from_yaml(config_file)
|
28
|
+
load(config_file)
|
28
29
|
configatron.paths.map! { |p| File.expand_path(p) }
|
29
|
-
configatron.ignore_rules.map! { |a| Regexp.new(a) }
|
30
30
|
|
31
31
|
# An array which lists the directories where mnts are stored.
|
32
32
|
@paths = configatron.paths
|
33
33
|
# These are the rules that are checked to see if a file in a mnt
|
34
34
|
# should be ignored.
|
35
35
|
@ignore_rules = configatron.ignore_rules
|
36
|
-
|
37
|
-
@collection_links = configatron.collection_links
|
38
|
-
@collection_link_prefix = configatron.collection_link_prefix
|
39
36
|
end
|
40
37
|
|
41
38
|
# Create a template configuration file at <em>config_file</em> after
|
@@ -47,16 +44,23 @@ module Sundae
|
|
47
44
|
ans = gets.downcase.strip
|
48
45
|
if ans == "y" || ans == "yes"
|
49
46
|
File.open(config_file, "w") do |f|
|
50
|
-
f.puts
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
47
|
+
f.puts <<-EOM.gsub(/^ {14}/, '')
|
48
|
+
# -*-Ruby-*-
|
49
|
+
|
50
|
+
# An array which lists the directories where mnts are stored.
|
51
|
+
configatron.paths = ["~/mnt"]
|
52
|
+
|
53
|
+
# These are the rules that are checked to see if a file in a mnt
|
54
|
+
# should be ignored.
|
55
|
+
#
|
56
|
+
# For `ignore_rules', use either strings (can be globs)
|
57
|
+
# or Ruby regexps. You can mix both in the same array.
|
58
|
+
# Globs are matched using the method File.fnmatch.
|
59
|
+
configatron.ignore_rules = %w(.git,
|
60
|
+
.bzr,
|
61
|
+
.svn,
|
62
|
+
.DS_Store)
|
63
|
+
EOM
|
60
64
|
end
|
61
65
|
puts
|
62
66
|
puts "Okay then."
|
@@ -74,7 +78,13 @@ module Sundae
|
|
74
78
|
def self.ignore_file?(file) # :doc:
|
75
79
|
return true if File.basename(file) =~ /^\.\.?$/
|
76
80
|
return true if File.basename(file) == ".sundae_path"
|
77
|
-
@ignore_rules.each
|
81
|
+
@ignore_rules.each do |r|
|
82
|
+
if r.kind_of? Regexp
|
83
|
+
return true if File.basename(file) =~ r
|
84
|
+
else
|
85
|
+
return true if File.fnmatch(r, file)
|
86
|
+
end
|
87
|
+
end
|
78
88
|
return false
|
79
89
|
end
|
80
90
|
|
@@ -94,12 +104,7 @@ module Sundae
|
|
94
104
|
# be created.
|
95
105
|
#
|
96
106
|
def self.install_locations
|
97
|
-
|
98
|
-
|
99
|
-
all_mnts.each do |mnt|
|
100
|
-
locations << install_location(mnt)
|
101
|
-
end
|
102
|
-
return locations.sort.uniq
|
107
|
+
all_mnts.map { |m| install_location(m) }.sort.uniq
|
103
108
|
end
|
104
109
|
|
105
110
|
# Given _path_, return all mnts (i.e., directories two levels down)
|
@@ -111,7 +116,7 @@ module Sundae
|
|
111
116
|
collections.each do |c|
|
112
117
|
collection_mnts = Dir.entries(File.join(path, c)).delete_if {|a| a=~/^\./}
|
113
118
|
collection_mnts.map! { |mnt| File.join(c, mnt) }
|
114
|
-
mnts |= collection_mnts
|
119
|
+
mnts |= collection_mnts # |= is the union
|
115
120
|
end
|
116
121
|
|
117
122
|
return mnts.sort.uniq
|
@@ -124,7 +129,7 @@ module Sundae
|
|
124
129
|
|
125
130
|
@paths.each do |path|
|
126
131
|
next unless File.exist?(path)
|
127
|
-
mnts |= mnts_in_path(path).map { |mnt| File.join(path, mnt) }
|
132
|
+
mnts |= mnts_in_path(path).map { |mnt| File.join(path, mnt) } # |= is the union operator
|
128
133
|
end
|
129
134
|
|
130
135
|
return mnts
|
@@ -143,7 +148,7 @@ module Sundae
|
|
143
148
|
end
|
144
149
|
end
|
145
150
|
|
146
|
-
return dirs.sort.uniq
|
151
|
+
return dirs.sort.uniq.select { |d| File.directory?(d) }
|
147
152
|
end
|
148
153
|
|
149
154
|
# Check for symlinks in the base directories that are missing their
|
@@ -172,7 +177,7 @@ module Sundae
|
|
172
177
|
generated_directories.each do |dir|
|
173
178
|
next if File.basename(dir) == ('.sundae')
|
174
179
|
|
175
|
-
# Do a
|
180
|
+
# Do a search to make sure no non-symlink file is being
|
176
181
|
# deleted. That would suck.
|
177
182
|
if sf = find_static_file(dir)
|
178
183
|
puts "found static file: #{sf}"
|
@@ -229,18 +234,6 @@ module Sundae
|
|
229
234
|
|
230
235
|
Find.prune if File.directory?(path)
|
231
236
|
end
|
232
|
-
create_collection_links(target, link_path)
|
233
|
-
end
|
234
|
-
|
235
|
-
# Create links in a generated mirror directory to the analogous
|
236
|
-
# location in the mounted directories.
|
237
|
-
#
|
238
|
-
def self.create_collection_links(target, link_name)
|
239
|
-
return unless @collection_links
|
240
|
-
|
241
|
-
collection_name = File.basename(root_path(target))
|
242
|
-
collection_link = File.join(link_name, @collection_link_prefix + collection_name)
|
243
|
-
create_link(target, collection_link) unless File.exist? collection_link
|
244
237
|
end
|
245
238
|
|
246
239
|
# Starting at _dir_, walk up the directory hierarchy and return the
|
@@ -250,11 +243,7 @@ module Sundae
|
|
250
243
|
raise ArgumentError if dir == '/'
|
251
244
|
|
252
245
|
parent = File.expand_path(File.join(dir, '..'))
|
253
|
-
|
254
|
-
return dir
|
255
|
-
else
|
256
|
-
root_path parent
|
257
|
-
end
|
246
|
+
return (@paths.include?(parent)) ? dir : root_path(parent)
|
258
247
|
end
|
259
248
|
|
260
249
|
# Dispatch calls to create_directory_link and create_file_link.
|
@@ -326,5 +315,78 @@ module Sundae
|
|
326
315
|
minimally_create_links(target_path2, link_name)
|
327
316
|
end
|
328
317
|
|
318
|
+
def self.update_filesystem
|
319
|
+
remove_dead_links
|
320
|
+
remove_generated_directories
|
321
|
+
create_filesystem
|
322
|
+
end
|
323
|
+
|
324
|
+
def self.remove_filesystem
|
325
|
+
remove_dead_links
|
326
|
+
remove_generated_directories
|
327
|
+
end
|
328
|
+
|
329
|
+
# Return an array of mnts that are installing to +path+.
|
330
|
+
#
|
331
|
+
def self.find_source_directories(path)
|
332
|
+
sources = Array.new
|
333
|
+
all_mnts.each do |mnt|
|
334
|
+
install_location = File.expand_path(install_location(mnt))
|
335
|
+
if path.include?(install_location)
|
336
|
+
relative_path = path.sub(Regexp.new(install_location), "")
|
337
|
+
sources << mnt if File.exist?(File.join(mnt, relative_path))
|
338
|
+
end
|
339
|
+
end
|
340
|
+
return sources
|
341
|
+
end
|
342
|
+
|
343
|
+
# Move the file at +path+ (or its target in the case of a link) to
|
344
|
+
# +mnt+ preserving relative path.
|
345
|
+
#
|
346
|
+
def self.move_to_mnt(path, mnt)
|
347
|
+
if File.symlink?(path)
|
348
|
+
to_move = File.readlink(path)
|
349
|
+
current = Sundae.find_source_directories(path)[0]
|
350
|
+
relative_path = to_move.sub(Regexp.new(current), "")
|
351
|
+
FileUtils.mv(to_move, mnt + relative_path) unless current == mnt
|
352
|
+
FileUtils.ln_sf(mnt + relative_path, path)
|
353
|
+
else
|
354
|
+
location = Sundae.install_location(mnt)
|
355
|
+
relative_path = path.sub(Regexp.new(location), "")
|
356
|
+
FileUtils.mv(path, mnt + relative_path) unless path == mnt + relative_path
|
357
|
+
FileUtils.ln_s(mnt + relative_path, path)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# Move the target at +link+ according to +relative_path+.
|
362
|
+
#
|
363
|
+
def self.move_to_relative_path(link, relative_path)
|
364
|
+
raise ArgumentError, "#{link} is not a link." unless File.symlink?(link)
|
365
|
+
|
366
|
+
target = File.readlink(link)
|
367
|
+
|
368
|
+
pwd = FileUtils.pwd
|
369
|
+
mnt = Sundae.find_source_directories(link)[0]
|
370
|
+
mnt_pwd = File.join(mnt, pwd.sub(Regexp.new(install_location(mnt)), ""))
|
371
|
+
|
372
|
+
if File.directory?(relative_path)
|
373
|
+
new_target_path = File.join(mnt_pwd, relative_path, File.basename(link))
|
374
|
+
new_link_path = File.join(pwd, relative_path, File.basename(link))
|
375
|
+
else
|
376
|
+
new_target_path = File.join(mnt_pwd, relative_path)
|
377
|
+
new_link_path = File.join(pwd, relative_path)
|
378
|
+
end
|
379
|
+
|
380
|
+
target = File.expand_path(target)
|
381
|
+
new_target_path = File.expand_path(new_target_path)
|
382
|
+
new_link_path = File.expand_path(new_link_path)
|
383
|
+
|
384
|
+
raise ArgumentError, "#{link} and #{new_target_path} are the same file" if target == new_target_path
|
385
|
+
FileUtils.mkdir_p(File.dirname(new_target_path))
|
386
|
+
FileUtils.mv(target, new_target_path)
|
387
|
+
FileUtils.rm(link)
|
388
|
+
FileUtils.ln_s(new_target_path, new_link_path)
|
389
|
+
end
|
390
|
+
|
329
391
|
end
|
330
392
|
|