lib/discourse_theme/cli.rb in discourse_theme-0.1.8 vs lib/discourse_theme/cli.rb in discourse_theme-0.2.0

- old
+ new

@@ -1,123 +1,148 @@ -class DiscourseTheme::Cli +module DiscourseTheme + class Cli - SETTINGS_FILE = File.expand_path("~/.discourse_theme") + @@prompt = ::TTY::Prompt.new(help_color: :cyan) + @@pastel = Pastel.new - def usage - puts "Usage: discourse_theme COMMAND" - puts - puts "discourse_theme new DIR : Creates a new theme in the designated directory" - puts "discourse_theme watch DIR : Watches the theme directory and synchronizes with Discourse" - exit 1 - end + def self.yes?(message) + @@prompt.yes?(@@pastel.cyan("? ") + message) + end - def guess_api_key(settings) - api_key = ENV['DISCOURSE_API_KEY'] - if api_key - puts "Using api_key provided by DISCOURSE_API_KEY" + def self.ask(message, default: nil) + @@prompt.ask(@@pastel.cyan("? ") + message, default: default) end - if !api_key && settings.api_key - api_key = settings.api_key - puts "Using previously stored api key in #{SETTINGS_FILE}" + def self.select(message, options) + @@prompt.select(@@pastel.cyan("? ") + message, options) end - if !api_key - puts "No API key found in DISCOURSE_API_KEY env var enter your API key: " - api_key = STDIN.gets.strip - puts "Would you like me to store this API key in #{SETTINGS_FILE}? (Yes|No)" - answer = STDIN.gets.strip - if answer =~ /y(es)?/i - settings.api_key = api_key - end + def self.info(message) + puts @@pastel.blue("i ") + message end - api_key - end + def self.progress(message) + puts @@pastel.yellow("» ") + message + end - def is_https_redirect?(url) - url = URI.parse(url) - path = url.path - path = "/" if path.empty? - req = Net::HTTP::Get.new("/") - response = Net::HTTP.start(url.host, url.port) { |http| http.request(req) } - Net::HTTPRedirection === response && response['location'] =~ /^https/i - end + def self.error(message) + puts @@pastel.red("✘ #{message}") + end - def guess_url(settings) - url = ENV['DISCOURSE_URL'] - if url - puts "Site provided by DISCOURSE_URL" + def self.success(message) + puts @@pastel.green("✔ #{message}") end - if !url && settings.url - url = settings.url - puts "Using #{url} defined in #{SETTINGS_FILE}" + SETTINGS_FILE = File.expand_path("~/.discourse_theme") + + def usage + puts "Usage: discourse_theme COMMAND [--reset]" + puts + puts "discourse_theme new DIR : Creates a new theme in the designated directory" + puts "discourse_theme download DIR : Download a theme from the server, and store in the designated directory" + puts "discourse_theme watch DIR : Watches the theme directory and synchronizes with Discourse" + puts + puts "Use --reset to change the configuration for a directory" + exit 1 end - if !url - puts "No site found! Where would you like to synchronize the theme to: " - url = STDIN.gets.strip - url = "http://#{url}" unless url =~ /^https?:\/\// + def run(args) + usage unless args[1] - # maybe this is an HTTPS redirect - uri = URI.parse(url) - if URI::HTTP === uri && uri.port == 80 && is_https_redirect?(url) - puts "Detected an #{url} is an HTTPS domain" - url = url.sub("http", "https") - end + reset = !!args.delete("--reset") - puts "Would you like me to store this site name at: #{SETTINGS_FILE}? (Yes|No)" - answer = STDIN.gets.strip - if answer =~ /y(es)?/i - settings.url = url - end - end + command = args[0].to_s.downcase + dir = File.expand_path(args[1]) - url - end + config = DiscourseTheme::Config.new(SETTINGS_FILE) + settings = config[dir] - def run - usage unless ARGV[1] + theme_id = settings.theme_id - command = ARGV[0].to_s.downcase - dir = File.expand_path(ARGV[1]) + if command == "new" + raise DiscourseTheme::ThemeError.new "'#{dir} is not empty" if Dir.exists?(dir) && !Dir.empty?(dir) + DiscourseTheme::Scaffold.generate(dir) + if Cli.yes?("Would you like to start 'watching' this theme?") + args[0] = "watch" + Cli.progress "Running discourse_theme #{args.join(' ')}" + run(args) + end + elsif command == "watch" + raise DiscourseTheme::ThemeError.new "'#{dir} does not exist" unless Dir.exists?(dir) + client = DiscourseTheme::Client.new(dir, settings, reset: reset) - dir_exists = File.exist?(dir) + theme_list = client.get_themes_list - if command == "new" && !dir_exists - DiscourseTheme::Scaffold.generate(dir) - elsif command == "watch" && dir_exists - if !File.exist?("#{dir}/about.json") - puts "No about.json file found in #{dir}!" - puts - usage - end + options = {} + if theme_id && theme = theme_list.find { |t| t["id"] == theme_id } + options["Sync with existing theme: '#{theme["name"]}' (id:#{theme_id})"] = :default + end + options["Create and sync with a new theme"] = :create + options["Select a different theme"] = :select - config = DiscourseTheme::Config.new(SETTINGS_FILE) - settings = config[dir] + choice = Cli.select('How would you like to sync this theme?', options.keys) - url = guess_url(settings) - api_key = guess_api_key(settings) + if options[choice] == :create + theme_id = nil + elsif options[choice] == :select + themes = render_theme_list(theme_list) + choice = Cli.select('Which theme would you like to sync with?', themes) + theme_id = extract_theme_id(choice) + end - if !url - puts "Missing site to synchronize with!" - usage - end + uploader = DiscourseTheme::Uploader.new(dir: dir, client: client, theme_id: theme_id) - if !api_key - puts "Missing api key!" + Cli.progress "Uploading theme from #{dir}" + settings.theme_id = theme_id = uploader.upload_full_theme + + Cli.success "Theme uploaded (id:#{theme_id})" + watcher = DiscourseTheme::Watcher.new(dir: dir, uploader: uploader) + + Cli.progress "Watching for changes in #{dir}..." + watcher.watch + + elsif command == "download" + client = DiscourseTheme::Client.new(dir, settings, reset: reset) + downloader = DiscourseTheme::Downloader.new(dir: dir, client: client) + + FileUtils.mkdir_p dir unless Dir.exists?(dir) + raise DiscourseTheme::ThemeError.new "'#{dir} is not empty" unless Dir.empty?(dir) + + Cli.progress "Loading theme list..." + themes = render_theme_list(client.get_themes_list) + + choice = Cli.select('Which theme would you like to download?', themes) + theme_id = extract_theme_id(choice) + + Cli.progress "Downloading theme into #{dir}" + + downloader.download_theme(theme_id) + settings.theme_id = theme_id + + Cli.success "Theme downloaded" + + if Cli.yes?("Would you like to start 'watching' this theme?") + args[0] = "watch" + Cli.progress "Running discourse_theme #{args.join(' ')}" + run(args) + end + else usage end - uploader = DiscourseTheme::Uploader.new(dir: dir, api_key: api_key, site: url) - print "Uploading theme from #{dir} to #{url} : " - uploader.upload_full_theme + Cli.progress "Exiting..." + rescue DiscourseTheme::ThemeError => e + Cli.error "#{e.message}" + rescue Interrupt, TTY::Reader::InputInterrupt => e + Cli.error "Interrupted" + end - watcher = DiscourseTheme::Watcher.new(dir: dir, uploader: uploader) + def render_theme_list(themes) + themes.sort_by { |t| t["updated_at"] } + .reverse.map { |theme| "#{theme["name"]} (id:#{theme["id"]})" } + end - watcher.watch - else - usage + def extract_theme_id(rendered_name) + /\(id:([0-9]+)\)$/.match(rendered_name)[1].to_i end end end