mattbrictson/nextgen

View on GitHub
template/bin/setup

Summary

Maintainability
Test Coverage
#!/usr/bin/env ruby
# frozen_string_literal: true

# This script is a way to set up or update your development environment automatically.
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
# Add necessary setup steps to this method.
def setup!
  env ".env", from: ".env.sample"
  run "bundle install" if bundle_needed?
  run "overcommit --install" if overcommit_installable?
  run "bin/rails db:prepare" if database_present?
  run install_node_packages_command if node_present?
  run "bin/rails tmp:create" if tmp_missing?
  run "bin/rails restart" if pid_present?

  if git_safe_needed?
    say_status :notice,
      "Remember to run #{colorize("mkdir -p .git/safe", :yellow)} to trust the binstubs in this project",
      :magenta
  end

  say_status :Ready!,
    "Use #{colorize("bin/dev", :yellow)} to start the app, " \
    "or #{colorize("bin/rake", :yellow)} to run tests"
end

def run(command, echo: true, silent: false, exception: true)
  say_status(:run, command, :blue) if echo
  with_original_bundler_env do
    options = silent ? {out: File::NULL, err: File::NULL} : {}
    system(command, exception:, **options)
  end
end

def run?(command)
  run command, silent: true, echo: false, exception: false
end

def bundle_needed?
  !run("bundle check", silent: true, exception: false)
end

def overcommit_installable?
  File.exist?(".overcommit.yml") && !File.exist?(".git/hooks/overcommit-hook") && run?("overcommit -v")
end

def database_present?
  File.exist?("config/database.yml")
end

def node_present?
  File.exist?("package.json")
end

def install_node_packages_command
  if File.exist?("yarn.lock")
    "yarn install --check-files"
  else
    "npm install"
  end
end

def tmp_missing?
  !Dir.exist?("tmp/pids")
end

def pid_present?
  Dir["tmp/pids/*.pid"].any?
end

def git_safe_needed?
  ENV["PATH"].include?(".git/safe/../../bin") && !Dir.exist?(".git/safe")
end

def with_original_bundler_env(&)
  return yield unless defined?(Bundler)

  Bundler.with_original_env(&)
end

def env(env_file, from:)
  return unless File.exist?(from)

  unless File.exist?(env_file)
    say_status(:copy, "#{from} → #{env_file}", :magenta)
    require "fileutils"
    FileUtils.cp(from, env_file)
  end

  keys = ->(f) { File.readlines(f).filter_map { |l| l[/^([^#\s][^=\s]*)/, 1] } }

  missing = keys[from] - keys[env_file]
  return if missing.empty?

  say_status(:WARNING, "Your #{env_file} file is missing #{missing.join(", ")}. Refer to #{from} for details.", :red)
end

def say_status(label, message, color = :green)
  label = label.to_s.rjust(12)
  puts [colorize(label, color), message.gsub(/^/, " " * 13).strip].join(" ")
end

def colorize(str, color)
  return str unless color_supported?

  code = {red: 31, green: 32, yellow: 33, blue: 34, magenta: 35}.fetch(color)
  "\e[0;#{code};49m#{str}\e[0m"
end

def color_supported?
  if ENV["TERM"] == "dumb" || !ENV["NO_COLOR"].to_s.empty?
    false
  else
    [$stdout, $stderr].all? { |io| io.respond_to?(:tty?) && io.tty? }
  end
end

Dir.chdir(File.expand_path("..", __dir__)) do
  setup!
end