#!/usr/bin/env ruby require File.expand_path(File.dirname(__FILE__)+ '/../lib/s3_website') require 'colored' require 'digest/md5' class Cfg < Thor desc 'create', 'Create a config file with placeholder values' def create config_file_src = File.dirname(__FILE__) + '/../resources/configuration_file_template.yml' config_file = Dir.pwd + '/s3_website.yml' unless File.exists? config_file require 'fileutils' FileUtils.cp config_file_src, config_file puts "Created the config file s3_website.yml. Go fill it with your settings." end rescue Exception => error puts "#{error.message} (#{error.class})" exit 1 end desc 'apply', 'Apply the configuration on the AWS services' long_desc <<-LONGDESC `s3_website cfg apply` will apply the configuration the S3 bucket. In addition, if you CloudFront related settings, this command will apply them on the CloudFront distribution. If the S3 bucket does not exist, this command will create it and configure it to function as a website. LONGDESC def apply puts 'Applying the configurations in s3_website.yml on the AWS services ...' require 'configure-s3-website' config_source = ConfigureS3Website::FileConfigSource.new 's3_website.yml' ConfigureS3Website::Runner.run({ :config_source => config_source }) rescue Exception => error puts "#{error.message} (#{error.class})" exit 1 end end class Cli < Thor option( :site, :type => :string, :desc => "The directory where your website files are. When not defined, s3_website will look for the site in either _site or public/output." ) option( 'config-dir'.to_sym, :type => :string, :desc => "The directory where your config file is. When not defined, s3_website will look in the current working directory." ) option( :verbose, :type => :boolean, :default => false, :desc => "Print verbose output" ) option( :dry_run, :type => :boolean, :default => false, :desc => "Run the operation without actually making the modifications. When this switch is on, changes will not be applied on the S3 website. They will be only printed to the console." ) desc 'push', 'Push local files with the S3 website' long_desc <<-LONGDESC `s3_website push` will upload new and changes files to S3. It will also delete from S3 the files that you no longer have locally. LONGDESC def push project_root = File.expand_path(File.dirname(__FILE__)+ '/..') logger = Logger.new(options[:verbose]) success = if run_with_sbt(project_root) Dir.chdir(project_root) { system './sbt assembly' # Build the jar } system "java -cp #{project_root}/target/scala-2.11/s3_website.jar #{resolve_java_command 'push'}" else # Find the jar jar_file = resolve_jar(project_root, logger) # Then run it run_s3_website_jar(jar_file, logger) end if success exit 0 else exit 1 end end desc 'cfg SUBCOMMAND ...ARGS', 'Operate on the config file' subcommand 'cfg', Cfg end def run_with_sbt(project_root) File.exists?(project_root + '/project/sbt-launch.jar') end def resolve_java_command(command_name) args = ARGV.join(' ').sub(command_name, '') "s3.website.#{command_name.capitalize} #{args}" end def run_s3_website_jar(jar_file, logger) java_installed = resolve_exit_status('which java') or resolve_exit_status('java -version') unless java_installed logger.info_msg "Cannot find Java. s3_website push is implemented in Scala, and it needs Java to run." autoinstall_java_or_print_help_and_exit(logger) end logger.debug_msg "Using #{jar_file}" system("java -cp #{jar_file} #{resolve_java_command 'push'}") end def resolve_exit_status(cmd) `#{cmd}` cmd_succeeded = $? == 0 rescue cmd_succeeded = false end def autoinstall_java_or_print_help_and_exit(logger) @logger = logger automatic_methods = [ { :package_manager_lookup => 'which apt-get', :install_command => 'sudo apt-get install --assume-yes openjdk-7-jre' }, { :package_manager_lookup => 'which yum', :install_command => 'sudo yum install --assumeyes java-1.7.0-openjdk' } ] def print_manual_method_and_exit @logger.info_msg 'Go to http://java.com, install Java and then try again.' @logger.info_msg "(If you cannot or do not want to install Java, you can use latest 1.x version of this gem, which requires only Ruby. For more info, see https://github.com/laurilehmijoki/s3_website/tree/1.x)" exit 1 end manual_method_help = automatic_method = automatic_methods.find { |automatic_method| resolve_exit_status automatic_method.fetch(:package_manager_lookup) } if automatic_method @logger.info_msg "Do you want me to install Java with the command `#{automatic_method.fetch(:install_command)}`? [Y/n]" user_answer = $stdin.gets if user_answer.chomp.downcase == 'y' or user_answer == "\n" automatic_method_succeeded = system automatic_method.fetch(:install_command) unless automatic_method_succeeded @logger.fail_msg "Could not automatically install Java. Try setting it up manually:" print_manual_method_and_exit end else print_manual_method_and_exit end else print_manual_method_and_exit end end def resolve_jar(project_root, logger) jar_lookup_paths = [ project_root + "/s3_website-#{S3Website::VERSION}.jar", (ENV['TMPDIR'] || '/tmp') + "/s3_website-#{S3Website::VERSION}.jar" ] found_jar = jar_lookup_paths. select { |jar_path| File.exists? jar_path }. first def jar_has_valid_checksum(jar_path, logger) expected_checksum = File.read(File.dirname(__FILE__) + '/../resources/s3_website.jar.md5') found_checksum = Digest::MD5.file(jar_path).hexdigest if expected_checksum == found_checksum true else logger.info_msg "The jar file is corrupted. (Expected checksum #{expected_checksum} but got #{found_checksum}.)" false end end jar_file = if found_jar and jar_has_valid_checksum(found_jar, logger) found_jar else download_jar(jar_lookup_paths, logger) end end def download_jar(jar_lookup_paths, logger) tag_name = "v#{S3Website::VERSION}" downloaded_jar = jar_lookup_paths.select { |jar_path| File.writable? File.dirname(jar_path) }.first unless downloaded_jar logger.fail_msg "Neither #{jar_lookup_paths.join ' or '} is writable. Cannot download s3_website.jar." logger.fail_msg "Set either directory as writable to the current user and try again." exit 1 end download_url = "https://github.com/laurilehmijoki/s3_website/releases/download/#{tag_name}/s3_website.jar" logger.info_msg "Downloading #{download_url} into #{downloaded_jar}" require 'open-uri' open(downloaded_jar, 'wb') do |file| file << open(download_url).read end downloaded_jar end class Logger attr_reader :verbose def initialize(verbose) @verbose = verbose end def debug_msg(msg) if verbose print_msg 'debg'.cyan, msg end end def info_msg(msg) print_msg 'info'.blue, msg end def fail_msg(msg) print_msg 'fail'.red, msg end private def print_msg(prefix, msg) puts "[#{prefix}] #{msg}" end end Cli.start(ARGV)