Skip to content

Instantly share code, notes, and snippets.

@tsutsu
Last active February 2, 2020 03:13
Show Gist options
  • Save tsutsu/74c8dccd03c8914273171ad84a83859c to your computer and use it in GitHub Desktop.
Save tsutsu/74c8dccd03c8914273171ad84a83859c to your computer and use it in GitHub Desktop.

This is an example of a script for new systems, which 'mounts' dotfiles from a cloud-synced directory, and also moves junk out of the homedir into the places where it "should" live as per various filesystem standards (Linux FHS, FreeDesktop.org, macOS, etc.), replacing the origins with symlinks to the new locations.

The script is designed to be idempotent; it could theoretically be run on startup.

Various locations are hard-coded. Please take this script only as inspiration; don't try to run it directly.

#!/usr/bin/env ruby
require 'fileutils'
require 'pathname'
require 'paint'
require 'set'
$overwrite = ARGV.delete('-o') or ARGV.delete('--overwrite')
$dry_run = ARGV.delete('-d') or ARGV.delete('--dry-run')
class Pathname
def cp_r(dest); FileUtils.cp_r(self.to_s, dest.to_s); end
META_FILES = Set['.DS_Store'].freeze
def regular_children; children.find_all{ |ch| not META_FILES.include?(ch.basename.to_s) }; end
def empty_directory?; directory? and regular_children.empty?; end
def populated_directory?; directory? and not regular_children.empty?; end
end
if $dry_run
class Pathname
def unlink; $stderr.puts Paint["would unlink '#{self}'", :blue]; end
def mkpath; $stderr.puts Paint["would mkpath '#{self}'", :blue]; end
def rmtree; $stderr.puts Paint["would rmtree '#{self}'", :blue]; end
def make_symlink(src); $stderr.puts Paint["would symlink '#{src}' <- '#{self}'", :blue]; end
def cp_r(dest); $stderr.puts Paint["would recursively copy '#{self}' -> '#{dest}'", :blue]; end
end
end
home_dir = Pathname.new(ENV['HOME'])
bundles_dir = Pathname.new(__FILE__).parent.parent.expand_path
bins_dir = home_dir + '.bins'
caches_dir = home_dir + 'Library' + 'Caches'
posix_bundle = bundles_dir + 'POSIX.bundle'
freedesktop_bundle = bundles_dir + 'FreeDesktop.bundle'
scripts_bundle = bundles_dir + 'Scripts.bundle'
attach_map = {
(home_dir + '.local' + 'share') => (freedesktop_bundle + 'share'),
(home_dir + '.local' + 'bin') => (freedesktop_bundle + 'bin'),
(home_dir + '.config') => (freedesktop_bundle + 'config'),
(home_dir + '.cache') => (caches_dir),
(bins_dir + 'freedesktop') => (home_dir + '.local' + 'bin'),
(bins_dir + 'scripts') => (scripts_bundle + 'bin')
}
posix_bundle.children.each do |dotfile|
attach_path = home_dir + ('.' + dotfile.basename.to_s)
attach_map[attach_path] = dotfile
end
attach_map.each do |attach_path, dest|
unless dest.exist?
$stderr.puts Paint["! not attaching non-existent component path '#{dest}'", :red]
next
end
attach_path.parent.mkpath unless attach_path.parent.exist?
if dest.basename.to_s =~ /.template/
copy_path = attach_path.parent + attach_path.basename.to_s.gsub(/\.template$/, '')
if $overwrite
$stderr.puts Paint["! deleting source '#{copy_path}' to overwrite with template", :yellow]
copy_path.rmtree if copy_path.exist?
else
next if copy_path.exist?
end
copy_path.unlink if copy_path.symlink?
FileUtils.cp_r(dest.to_s, copy_path.to_s)
$stderr.puts Paint[" Copied template '#{dest}' to '#{copy_path}'", :green]
else
if attach_path.exist? and (not attach_path.symlink?) and (not $overwrite)
$stderr.puts Paint["! FreeDesktop dir '#{attach_path}' already exists; cannot attach", :red]
next
end
if attach_path.symlink?
attach_path.unlink
elsif attach_path.exist?
$stderr.puts Paint["! deleting source '#{attach_path}' to make link", :yellow]
attach_path.rmtree
end
attach_path.make_symlink(dest)
$stderr.puts Paint[" Linked '#{dest}' <= '#{attach_path}'", :green]
end
end
cache_map = {
(home_dir + '.cocoapods' + 'repos') => (caches_dir + 'CocoaPods' + 'repos'),
(home_dir + '.bundle' + 'cache') => (caches_dir + 'org.rubygems' + 'bundler'),
(home_dir + '.gem' + 'ruby') => (caches_dir + 'org.rubygems' + 'gem' + 'ruby'),
(home_dir + '.hex' + 'packages') => (caches_dir + 'pm.hex' + 'packages'),
(home_dir + '.docker' + 'machine' + 'cache') => (caches_dir + 'com.docker' + 'machine' + 'cache'),
(home_dir + '.npm') => (caches_dir + 'com.npmjs.npm'),
(home_dir + '.cargo' + 'bin') => (caches_dir + 'rs.rustup' + 'bin'),
(home_dir + '.cargo' + 'registry') => (caches_dir + 'io.crates' + 'registry'),
(home_dir + '.xargo' + 'lib' + 'rustlib') => (caches_dir + 'io.github.japaric.xargo' + 'rustlib'),
(home_dir + '.rustup' + 'toolchains') => (caches_dir + 'rs.rustup' + 'toolchains'),
(home_dir + '.wine' + 'drive_c' + 'windows') => (caches_dir + 'org.winehq.wine' + 'default' + 'windows'),
(home_dir + '.fontconfig') => (caches_dir + 'fontconfig'),
(home_dir + '.node-gyp') => (caches_dir + 'org.nodejs.node-gyp'),
(home_dir + '.heroku') => (caches_dir + 'com.heroku.heroku-toolbelt' + 'home'),
(home_dir + '.local' + 'share' + 'heroku') => (caches_dir + 'com.heroku.heroku-toolbelt' + 'share'),
(bins_dir + 'rust') => (caches_dir + 'rs.rustup' + 'bin')
}
cache_map.each do |old_path, new_path|
old_path.unlink if old_path.symlink?
new_path.parent.mkpath
if old_path.populated_directory? and new_path.populated_directory?
$stderr.puts Paint["! Cache dest dir '#{new_path}' already exists and has content; not replacing (merge manually)", :red]
next
end
new_path.rmtree if new_path.empty_directory?
old_path.rename(new_path) if old_path.exist?
new_path.mkpath unless new_path.exist?
old_path.parent.mkpath
old_path.make_symlink(new_path)
$stderr.puts Paint[" Redirected '#{old_path}' to '#{new_path}'", :green]
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment