class Gistribute::CLI
Public Class Methods
new()
click to toggle source
# File lib/cli.rb, line 9 def initialize @options = OptimistXL.options do version "gistribute #{File.read(File.expand_path('../VERSION', __dir__)).strip}" banner <<~BANNER #{version} Usage: gistribute SUBCOMMAND [OPTION]... INPUT Try `gistribute SUBCOMMAND -h` for more info. Available subcommands: * login: log into your GitHub account * logout: log out of your GitHub account * install: download and install files from a gistribution * upload: upload a new gistribution Options: BANNER opt :version, "display version number" opt :help, "display a help message" # Sub-commands can't access the version from this scope for whatever reason v = version subcmd :login, "log into your GitHub account" subcmd :logout, "log out of your GitHub account" subcmd :install, "install from a gistribution" do banner <<~BANNER #{v} Usage: gistribute install [OPTION]... URL_OR_ID Options: BANNER opt :yes, "install files without prompting" opt :force, "overwrite existing files without prompting" end subcmd :upload, "upload a gistribution" do banner <<~BANNER #{v} Usage: gistribute upload [OPTION]... FILE... gistribute upload [OPTION]... DIRECTORY Options: BANNER opt :description, "description for the Gist", type: :string opt :private, "use a private Gist" opt :yes, "upload files without prompting" end educate_on_error end @subcommand, @global_options, @subcommand_options = @options.subcommand, @options.global_options, @options.subcommand_options authenticate unless @subcommand == "logout" case @subcommand when "install" @gist_input = ARGV.first when "upload" @files = ARGV.dup end end
Public Instance Methods
authenticate()
click to toggle source
# File lib/cli/auth.rb, line 5 def authenticate access_token = if File.exist?(CONFIG_FILE) File.read(CONFIG_FILE).strip else device_res = URI.decode_www_form( Net::HTTP.post_form( URI("https://github.com/login/device/code"), "client_id" => CLIENT_ID, "scope" => "gist" ).body ).to_h retry_interval = device_res["interval"].to_i Launchy.open(device_res["verification_uri"]) puts <<~EOS Opening GitHub, please enter the authentication code: #{device_res['user_code']} If your browser did not open, visit #{device_res['verification_uri']} EOS uri = URI("https://github.com/login/oauth/access_token") # Keep trying until the user enters the code or the device code expires token = nil loop do sleep(retry_interval) response = URI.decode_www_form( Net::HTTP.post_form( uri, "client_id" => CLIENT_ID, "device_code" => device_res["device_code"], "grant_type" => "urn:ietf:params:oauth:grant-type:device_code" ).body ).to_h if (token = response["access_token"]) File.write(CONFIG_FILE, token) break elsif response["error"] == "authorization_pending" # The user has not yet entered the code; keep waiting silently next elsif response["error"] == "expired_token" panic! "Token expired! Please try again." else panic! response["error_description"] end end token end @client = Octokit::Client.new(access_token:) success_message = "Logged in as #{@client.user.login}." if @subcommand == "login" puts success_message.green else puts success_message puts end end
confirm?(prompt)
click to toggle source
# File lib/cli.rb, line 99 def confirm?(prompt) print prompt input = $stdin.gets.strip.downcase input.start_with?("y") || input.empty? end
get_input(prompt)
click to toggle source
# File lib/cli.rb, line 106 def get_input(prompt) print prompt $stdin.gets.strip end
install()
click to toggle source
# File lib/cli/install.rb, line 5 def install print "Downloading data..." id = Gistribute.parse_id(ARGV.first) begin gist = @client.gist(id) rescue Octokit::Error => e $stderr.print <<~EOS.chop.red \rThere was an error downloading the requested Gist. The error is as follows: EOS $stderr.puts " #{e.response_status} #{JSON.parse(e.response_body)['message']}" $stderr.print "The ID that was queried is: ".red $stderr.puts id exit 1 end # Regular expression word wrap to keep lines less than 80 characters. Then # check to see if it's empty- if not, put newlines on each side so that it # will be padded when displayed in the output. gist_description = gist.description.gsub(/(.{1,79})(\s+|\Z)/, "\\1\n").strip gist_description = "\n#{gist_description}\n" unless gist_description.empty? # Process files files = gist.files.map do |filename, data| metadata = filename.to_s.split("||").map(&:strip) # | as path separator in the Gist's file name, as Gist doesn't allow the # usage of /. path = Gistribute.decode(metadata.last) # Default description is the name of the file. description = metadata.size == 1 ? File.basename(path) : metadata.first { description:, path:, content: data[:content] } end puts <<~EOS \rFinished downloading Gist from: #{gist.html_url} Gist uploaded by #{ gist.owner ? "user #{gist.owner[:login]}" : 'an anonymous user' }. #{gist_description} EOS unless @subcommand_options.yes puts "Files:" files.each do |f| print "#{f[:description]}: " unless f[:description].empty? puts f[:path] end end if @subcommand_options.yes || confirm?("\nWould you like to install these files? [Yn] ") puts unless @subcommand_options.yes files.each do |f| if File.exist?(f[:path]) && !@subcommand_options.force && !confirm?("File already exists: #{f[:path]}\nWould you like to overwrite it? [Yn] ") puts " #{'*'.red} #{f[:description]} skipped." next end # Handle directories that don't exist. FileUtils.mkdir_p File.dirname(f[:path]) File.write(f[:path], f[:content]) # If using `--yes`, we print the path in the this string rather than # above with the prompt puts " #{'*'.green} #{f[:description]} installed#{ @subcommand_options.yes ? " to: #{f[:path]}" : '.' }" end else puts "Aborting.".red end end
panic!(message)
click to toggle source
Prints an error message and exits the program.
# File lib/cli.rb, line 112 def panic!(message) $stderr.puts "#{'Error'.red}: #{message}" exit 1 end
process_file(file)
click to toggle source
# File lib/cli/upload.rb, line 33 def process_file(file) file = File.expand_path(file) if File.directory?(file) # Recursively process every file in the directory process_files( Dir.glob("#{file}/**/*", File::FNM_DOTMATCH).reject { |f| File.directory? f } ) else unless (content = File.read(file)) panic! "Files cannot be empty." end puts "File: #{file}" desc = get_input("Enter pretty file name (leave blank to use raw file name): ") # Return a hash directly for single files { "#{"#{desc} || " unless desc.empty?}#{Gistribute.encode file}" => { content: } } end end
process_files(files)
click to toggle source
# File lib/cli/upload.rb, line 27 def process_files(files) files.each_with_object({}) do |file, hash| hash.merge!(process_file file) end end
run()
click to toggle source
# File lib/cli.rb, line 83 def run case @subcommand when "login" # Do nothing, #authenticate is run from the constructor when "logout" FileUtils.rm_rf CONFIG_FILE puts "Logged out.".green else if ARGV.empty? OptimistXL.educate end eval @subcommand end end
upload()
click to toggle source
# File lib/cli/upload.rb, line 5 def upload files_json = process_files(@files) if files_json.empty? panic! "No files found, aborting." end if @subcommand_options.yes || confirm?("\nUpload these files? [Yn] ") gist = @client.create_gist({ description: "[gistribution] #{@subcommand_options.description}".strip, public: !@subcommand_options.private, files: files_json }) puts if @subcommand_options.yes print "Gistribution uploaded to: ".green puts gist.html_url else puts "Aborted.".red end end