#!/usr/bin/env ruby require 'open3' require 'io/console' # https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html#256-colors def reset(string) "\033[0m#{string}" end def yellow(string) "\u001b[33;1m#{string}\033[0m" end def blue(string) "\u001b[38;5;39;1m#{string}\033[0m" end def dark_blue(string) "\u001b[38;5;39;1m#{string}\033[0m" end def light_blue(string) "\u001b[38;5;117;1m#{string}\033[0m" end def red(string) "\u001b[38;5;196;1m#{string}\033[0m" end def green(string) "\u001b[32;1m#{string}\033[0m" end def system!(*args) system(*args) || abort(red "\n== Command #{args} failed ==") end def rbenv_system!(command) system!("echo '(. ~/.zshrc > /dev/null 2> /dev/null) && #{command}' | zsh") end def app_is_installed?(app_to_install, name_to_display = nil) app_name = name_to_display || app_to_install report "Checking if #{app_name} is already installed." app_location, = Open3.capture3(with_reloaded_zshrc("which #{app_to_install}")) is_installed = app_location.chomp != '' if is_installed report green "#{app_name} is already installed!" end is_installed end def confirm?(consent_message) wait(consent_message, "y") end def wait(message, expected_response = nil) puts yellow <<~TEXT ❓ #{message.strip} #{expected_response ? "Hit '#{expected_response}' to continue, or any other key to quit." : "(Hit 'q' to quit.)"} TEXT user_response = $stdin.getch.chomp if expected_response || user_response.downcase == "n" unless user_response.downcase == expected_response abort(red "OK, stopping here!") end end true end def explain(text) puts blue <<~TEXT 💬 #{text.strip} TEXT sleep 1 end def report(text) puts blue <<~TEXT 🤖 #{text.strip} TEXT sleep 1 end def ask(text) result = "" while result.strip.length < 1 puts blue <<~TEXT ❓ #{text.strip} TEXT result = gets.chomp space end return result end def heading(block) space space puts two_tone block space # wait "Ready to continue?" end def two_tone(block) one, two = block.lines [dark_blue(one), light_blue(two)].join end def center(block) padding_length = (80 - block.lines.map(&:chomp).map(&:length).max) / 2 padding = "" padding_length.times do padding += " " end block.lines.map do |line| "#{padding}#{line}" end.join end def space puts "" end def colorize(block) block.lines.map(&:chomp).map do |line| line.split("").map do |character| case character when "╗", "╔", "═", "║", "╚", "╝" light_blue character when "░" dark_blue character when "█" reset character else character end end.join end.join("\n") end def with_reloaded_zshrc(command) "(. ~/.zshrc > /dev/null 2> /dev/null) && (#{command})" end space space puts two_tone center <<-ART █▄▄ █░█ █░░ █░░ █▀▀ ▀█▀   ▀█▀ █▀█ ▄▀█ █ █▄░█ █▄█ █▄█ █▄▄ █▄▄ ██▄ ░█░   ░█░ █▀▄ █▀█ █ █░▀█ ART puts two_tone center <<-ART █▀█ █▀█ █▀▀ █▀ █▀▀ █▄░█ ▀█▀ █▀ █▀▀ █▀▄ ██▄ ▄█ ██▄ █░▀█ ░█░ ▄█ ART space puts colorize center <<-ART ██████╗░░█████╗░██╗██╗░░░░░░██████╗░░░███╗░░██╗███████╗░██╗░░░░░░░██╗ ██╔══██╗██╔══██╗██║██║░░░░░██╔════╝░░░████╗░██║██╔════╝░██║░░██╗░░██║ ██████╔╝███████║██║██║░░░░░╚█████╗░░░░██╔██╗██║█████╗░░░╚██╗████╗██╔╝ ██╔══██╗██╔══██║██║██║░░░░░░╚═══██╗░░░██║╚████║██╔══╝░░░░████╔═████║░ ██║░░██║██║░░██║██║███████╗██████╔╝██╗██║░╚███║███████╗░░╚██╔╝░╚██╔╝░ ╚═╝░░╚═╝╚═╝░░╚═╝╚═╝╚══════╝╚═════╝░╚═╝╚═╝░░╚══╝╚══════╝░░░╚═╝░░░╚═╝░░ ART space space sleep 1 explain "Welcome to our getting started script for Ruby on Rails on macOS!" sleep 1 i = 0 explain <<-TEXT The following tools and dependencies will be installed if you don't have them already installed: #{i += 1}. Homebrew + Xcode Command Line Tools #{i += 1}. Git #{i += 1}. rbenv #{i += 1}. nvm #{i += 1}. Node.js #{i += 1}. Yarn #{i += 1}. Redis #{i += 1}. PostgreSQL #{i += 1}. LibYAML #{i += 1}. Ruby #{i += 1}. Ruby on Rails TEXT confirm? "Ready to begin?" heading <<-ART █░█ █▀█ █▀▄▀█ █▀▀ █▄▄ █▀█ █▀▀ █░█░█   ▄█▄   ▀▄▀ █▀▀ █▀█ █▀▄ █▀▀   █▀▀ █░░ █ █▀█ █▄█ █░▀░█ ██▄ █▄█ █▀▄ ██▄ ▀▄▀▄▀   ░▀░   █░█ █▄▄ █▄█ █▄▀ ██▄   █▄▄ █▄▄ █ ART def install_brew explain "Homebrew is an industry-standard package manager we'll use to install many of the other packages that a Rails application depends on." explain "Homebrew itself requires Apple's Xcode CLI tools, which it will download automatically. However, it will prompt you for your user password while installing." wait "Ready?" report "Installing Homebrew." system! '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' report "We'll do the stuff they're talking about above automatically for you. 🦾" if `arch`.chomp == "arm64" system! "echo 'eval \"$(/opt/homebrew/bin/brew shellenv)\"' >> ~/.zshrc" else system! "echo 'eval \"$(/usr/local/bin/brew shellenv)\"' >> ~/.zshrc" end report yellow <<~TEXT OK, in order to continue we now need `brew` properly initialized in your terminal, so we're going to stop here for a second and you need to: 1. Copy and paste the following command in your terminal: source ~/.zshrc 2. Hit ⬆️ twice and hit ⏎ return to re-run this script. Don't worry, we'll skip the steps we've already completed. TEXT exit end install_brew unless app_is_installed?('brew', 'Homebrew') heading <<-ART █▀▀ █ ▀█▀ █▄█ █ ░█░ ART def git_installed? report "Checking if Git is installed." _, _, status = Open3.capture3(with_reloaded_zshrc("brew list git")) is_installed = status.to_s.include?("exit 0") if is_installed report green "Git is already installed!" end return is_installed end def install_git report "Installing Git." system! "brew install git" end install_git unless git_installed? heading <<-ART █▀█ █▄▄ █▀▀ █▄░█ █░█ █▀▄ █▄█ ██▄ █░▀█ ▀▄▀ ART def install_rbenv report "Installing rbenv." system! "brew install rbenv" system "rbenv init" system! "echo '\neval \"$(rbenv init - zsh)\"' >> ~/.zshrc" system! ". ~/.zshrc" end install_rbenv unless app_is_installed?('rbenv') heading <<-ART █▄░█ █░█ █▀▄▀█ █░▀█ ▀▄▀ █░▀░█ ART def nvm_installed? report "Checking if nvm is installed." _, _, status = Open3.capture3(with_reloaded_zshrc("test -f ~/.nvm/nvm.sh")) is_installed = status.to_s.include?("exit 0") if is_installed report green "nvm is already installed." end is_installed end def install_nvm report "Installing nvm." system! "curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash" end install_nvm unless nvm_installed? heading <<-ART █▄░█ █▀█ █▀▄ █▀▀ ░ ░░█ █▀ █░▀█ █▄█ █▄▀ ██▄ ▄ █▄█ ▄█ ART def node_installed? report "Checking if Node.js is installed." app_location, = Open3.capture3(with_reloaded_zshrc("which node")) is_installed = (app_location.chomp != '' && app_location.chomp != '/usr/local/bin/node') if is_installed report green "Node.js is already installed!" end is_installed end def install_node report "Installing Node.js." # nvm, node or npm command is not working without sourcing nvm and using commands at the same time, need to find the proper solution later system!(". ~/.nvm/nvm.sh && nvm install --lts") end install_node unless node_installed? heading <<-ART █▄█ ▄▀█ █▀█ █▄░█ ░█░ █▀█ █▀▄ █░▀█ ART def install_yarn report "Installing Yarn." system!(". ~/.nvm/nvm.sh && npm install -g yarn") end install_yarn unless app_is_installed?('yarn', 'Yarn') heading <<-ART █▀█ █▀▀ █▀▄ █ █▀ █▀▄ ██▄ █▄▀ █ ▄█ ART def install_redis report "Installing Redis." system!("brew install redis") system!("brew services start redis") end install_redis unless app_is_installed?('redis-server', 'Redis') heading <<-ART █▀█ █▀█ █▀ ▀█▀ █▀▀ █▀█ █▀▀ █▀ █▀█ █░░ █▀▀ █▄█ ▄█ ░█░ █▄█ █▀▄ ██▄ ▄█ ▀▀█ █▄▄ ART def install_postgres report "Installing PostgreSQL." system!("brew install postgresql@14") system!("brew services start postgresql@14") end install_postgres unless app_is_installed?('pg_ctl', 'PostgreSQL') heading <<-ART █░░ █ █▄▄ █▄█ ▄▀█ █▀▄▀█ █░░  █▄▄ █ █▄█ ░█░ █▀█ █░▀░█ █▄▄ ART def libyaml_installed? report "Checking if LibYAML is installed." _, _, status = Open3.capture3(with_reloaded_zshrc("brew list libyaml")) is_installed = status.to_s.include?("exit 0") if is_installed report green "LibYAML is already installed!" end return is_installed end def install_libyaml report "Installing LibYAML." system! with_reloaded_zshrc("brew install libyaml") end install_libyaml unless libyaml_installed? heading <<-ART █▀█ █░█ █▄▄ █▄█ █▀▄ █▄█ █▄█ ░█░ ART latest_stable_ruby_version, = Open3.capture3("rbenv install -l | grep -v - | tail -1") latest_stable_ruby_version = latest_stable_ruby_version.chomp report "The latest available Ruby is version #{latest_stable_ruby_version}." def ruby_version_installed?(ruby_version) report "Checking if Ruby #{ruby_version} is already installed." installed_versions, = Open3.capture3(with_reloaded_zshrc("rbenv versions | grep #{ruby_version}")) is_installed = installed_versions.length.positive? if is_installed report green "Ruby #{ruby_version} is already installed!" end is_installed end def install_ruby_version(version) report "Installing Ruby #{version}." system!("rbenv install #{version}") end install_ruby_version(latest_stable_ruby_version) unless ruby_version_installed?(latest_stable_ruby_version) def global_ruby_set_to_system? report "Checking that the global Ruby is coming from rbenv." ruby_type, = Open3.capture3(with_reloaded_zshrc("rbenv global")) global_ruby_version_type = ruby_type.chomp is_system_ruby = global_ruby_version_type == 'system' if is_system_ruby report green "Global Ruby is coming from rbenv." end is_system_ruby end def switch_global_ruby(ruby_version) report "Switching global Ruby to come from rbenv." system!("rbenv global #{ruby_version}") system!("rbenv rehash") system!(". ~/.zshrc") end switch_global_ruby(latest_stable_ruby_version) if global_ruby_set_to_system? system!(with_reloaded_zshrc("gem update bundler")) heading <<-ART █▀█ █░█ █▄▄ █▄█   █▀█ █▄░█   █▀█ ▄▀█ █ █░░ █▀ █▀▄ █▄█ █▄█ ░█░   █▄█ █░▀█   █▀▄ █▀█ █ █▄▄ ▄█ ART def rails_installed? report "Checking if Rails is installed." app_location, = Open3.capture3("which rails") is_installed = app_location.chomp.include?(".rbenv") if is_installed report green "Rails is already installed!" end is_installed end def install_rails report "Installing Rails." rbenv_system! "gem install rails" rbenv_system! "rbenv rehash" end install_rails unless rails_installed? heading <<-ART █▀▀ █ ▀█▀   █▀▀ █▀█ █▄░█ █▀▀ █ █▀▀ █░█ █▀█ ▄▀█ ▀█▀ █ █▀█ █▄░█ █▄█ █ ░█░   █▄▄ █▄█ █░▀█ █▀░ █ █▄█ █▄█ █▀▄ █▀█ ░█░ █ █▄█ █░▀█ ART def set_git_user_identity_key(key_name, display_key_name) report "#{display_key_name.capitalize} isn't configured." response = ask "Input your #{display_key_name}:" report "Setting your #{display_key_name} in your Git configuration." system!("git config --global --add user.#{key_name} '#{response}'") end def git_user_identity_key_configured?(key_name, display_key_name) report "Checking if your #{display_key_name} is in your Git configuration." value, = Open3.capture3("git config --global user.#{key_name}") is_configured = value.chomp.length.positive? if is_configured report green "Your #{display_key_name} is already configured." end is_configured end set_git_user_identity_key('name', 'full name') unless git_user_identity_key_configured?('name', 'full name') set_git_user_identity_key('email', 'email address') unless git_user_identity_key_configured?('email', 'email address') heading <<-ART ▄▀█ █▀▀ ▀█▀ █░█ ▄▀█ █░░   █▀█ ▄▀█ █ █░░ █▀   █▄░█ █▀▀ █░█░█ █▀█ █▄▄ ░█░ █▄█ █▀█ █▄▄   █▀▄ █▀█ █ █▄▄ ▄█   █░▀█ ██▄ ▀▄▀▄▀ ART explain green "OK! All the dependencies are now installed! If you've already got an existing Ruby on Rails application, you're probably good to go now. If you don't already have an application, we can help you get that up and running as well." confirm? "Do you want to generate a new Rails application?" app_name = ask "What would you like to call your application?" formatted_app_name = app_name.downcase.split.join('-') def generate_rails_app(formatted_app_name) report "Generating your application in `./#{formatted_app_name}`." rbenv_system!("rails new #{formatted_app_name} --database=postgresql --css=tailwind") end generate_rails_app(formatted_app_name) Dir.chdir(formatted_app_name) report green "Done!" report "Setting up the application database." rbenv_system!("rails db:create") report green "Your application was successfully generated!" heading <<-ART ▄▀█ █▀█ █▀▀   █▄█ █▀█ █░█   █▀█ █▀▀ ▄▀█ █▀▄ █▄█ ▀█ █▀█ █▀▄ ██▄   ░█░ █▄█ █▄█   █▀▄ ██▄ █▀█ █▄▀ ░█░ ░▄ ART sleep 1 server_loading_page_location = 'tmp/server-loading.html' system!("touch #{server_loading_page_location}") loading_page_file_content = " Waiting for Rails server to load ....

Rails server is compiling required files

It can take a solid full minute.

We will redirect you to \"http://localhost:3000\" automatically when server has loaded.

Hang tight!

" File.write(server_loading_page_location, loading_page_file_content) system!("open #{server_loading_page_location}") report "Starting Rails Server" begin rbenv_system!("rails s") ensure system!("rm #{server_loading_page_location}") end