Skip to content

Instantly share code, notes, and snippets.

@tijn
Last active August 8, 2019 08:51
Show Gist options
  • Save tijn/fa4ba9361a0eab9ef832e8f82e6dd231 to your computer and use it in GitHub Desktop.
Save tijn/fa4ba9361a0eab9ef832e8f82e6dd231 to your computer and use it in GitHub Desktop.
install-ruby-shim
#!/usr/bin/env ruby
require 'erb'
require 'optparse'
require 'pathname'
PROJECT_ROOT_FILES = %w(.ruby-version Gemfile .env)
$verbose = false
$force = false
$dry_run = false
optionparser = OptionParser.new do |parser|
parser.banner = "Usage: #{$0} [options] PATH"
parser.separator ''
parser.separator 'Install a shim in ~/bin for your Ruby project. Use this instead of a symlink if your script depends on bundler or rbenv.'
parser.separator ''
parser.on("-d", "--[no-]dry-run", "print shim to stdout; do not write to file") do |d|
$dry_run = d
end
parser.on("-f", "--[no-]force", "Overwrite shim if it already exists") do |f|
$force = f
end
parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
$verbose = v
end
parser.on_tail("-h", "--help", "Show this message") do
puts parser
exit
end
end
optionparser.parse!(ARGV)
if ARGV.empty?
$stderr.puts "PATH is missing."
$stderr.puts
abort optionparser.to_s
end
def info(*string)
$stderr.puts(*string) if $verbose
end
def detect_project_root(dir)
Pathname.new(dir).ascend do |path|
return path.to_s if PROJECT_ROOT_FILES.any? { |filename| File.exist?("#{path}/#{filename}") }
end
abort "Cannot find #{PROJECT_ROOT_FILES.join(' or ')} for executable."
end
class String
def lchomp(match)
if index(match) == 0
self[match.size..-1]
else
self.dup
end
end
end
# Set project_root, subdir, and basename
target = ARGV.first
full_path = File.absolute_path target
project_root = detect_project_root(full_path)
subdir = File.dirname(full_path).lchomp(project_root)
basename = File.basename(target)
info "project root = #{project_root}"
info "subdir = #{subdir}"
info "basename = #{basename}"
if project_root.start_with?(ENV['HOME'])
project_root = project_root.sub(ENV['HOME'], '$HOME')
info "normalized project root = #{project_root}"
end
# Generate the script
script = ERB.new(DATA.read).result(binding) # the call to #binding gives the template access to all vars
if $dry_run
puts script
exit 0
end
# Write the shim
shim = "#{ENV['HOME']}/bin/#{basename}"
abort "File #{shim} already exists. Use --force to overwrite it." if File.exist?(shim) && !$force
File.write(shim, script)
`chmod +x #{shim}`
__END__
#!/bin/sh
# A very very stupid shim that seems to work fine for my projects
# The good thing about it is that it:
#
# 1. is stupidly simple
# 2. does not change the working directory!
# 3. does not change anything besides two ENV variables
dir=<%= project_root %>
export RBENV_DIR=$dir
export BUNDLE_GEMFILE=$dir/Gemfile
$dir<%= subdir %>/$(basename "$0") "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment