lib/raykit/console.rb



# frozen_string_literal: true


require "optparse"
require "slop"
require_relative("./logging")

module Raykit
  # The implementation for the raykit console application

  class Console
    attr_accessor :opts

    def initialize
      @opts = Slop.parse do |o|
        o.string "-t", "--task", "rake task", default: "default"
        o.bool "-v", "--verbose", "print detailed output", default: false
        o.bool "-q", "--quiet", "minimal output", default: false
        o.bool "-s", "--stop", "stop on error", default: true
      end

      if opts.verbose?
        puts "options: #{Rainbow(@opts.to_hash).yellow.bright}"
        puts "arguments:#{Rainbow(@opts.arguments).yellow.bright}"
      end
    end

    def run
      verb = "usage"
      verb = @opts.arguments[0] if @opts.arguments.length.positive?
      case verb
      when "usage"
        usage
      when "add"
        add
      when "remove"
        remove
      when "show"
        show
      when "work"
        work
      when "import"
        import
      when "clean"
        clean
      when "sync_version"
        sync_version
      when "copy"
        copy
      when "pull"
        pull
      else
        puts "unrecognized command #{verb}"
        1
      end
    end

    def verb_descriptions
      { "add" => "add a git url",
        "remove" => "remove one or more git urls",
        "show" => "show git urls matching a specific pattern",
        "work" => "clone and rake a git repository",
        "import" => "import git urls matching a specific pattern",
        "clean" => "clean one or more working directories",
        "pull" => "preform git pull on one or more working directories",
        "sync_version" => "synchronize the version number between two files",
        "copy" => "copy a file" }
    end

    def verb_usage
      { "add" => "add GIT_URL",
        "remove" => "remove URL_PATTERN",
        "show" => "show URL_PATTERN",
        "work" => "work URL_PATTERN [--task RAKE_TASK]",
        "import" => "import URL_PATTERN",
        "clean" => "clean URL_PATTERN",
        "pull" => "pull URL_PATTERN",
        "sync_version" => "sync_version SOURCE_FILENAME DESTINATION_FILENAME",
        "copy" => "copy SOURCE DESTINATION" }
    end

    def usage
      puts "Usage: raykit VERB [GIT_URL|PATTERN] [OPTIONS]"
      verb_descriptions.each do |k, v|
        puts "#{k.ljust(15, " ")} - #{v}"
      end
      # puts @opts

      # puts "verbs: " + verbs.to_s

      0
    end

    def add
      if @opts.arguments.length < 2
        puts "add requires a url"
        return 1
      end
      url = @opts.arguments[1]
      if REPOSITORIES.include?(url)
        puts "url #{url} already exists."
      else
        REPOSITORIES << url
        REPOSITORIES.save(REPOSITORIES.filename)
        puts "url #{url} added."
      end
    end

    def remove
      if @opts.arguments.length < 2
        puts "remove requires a url or pattern"
        return 1
      end
      pattern = ""
      pattern = @opts.arguments[1] if @opts.arguments.length > 1
      remove_urls = REPOSITORIES.matches(pattern)
      if remove_urls.length.zero?
        puts "no matching urls found."
      else
        remove_urls.each do |url|
          REPOSITORIES.delete(url)
          puts "url #{url} removed."
        end
        REPOSITORIES.save(REPOSITORIES.filename)
      end
    end

    def show
      pattern = ""
      pattern = @opts.arguments[1] if @opts.arguments.length > 1
      REPOSITORIES.matches(pattern).each do |url|
        puts url
      end
    end

    def work
      pattern = ""
      pattern = @opts.arguments[1] if @opts.arguments.length > 1

      work_repositories = REPOSITORIES.matches(pattern)
      errors = []
      puts "work: found #{work_repositories.length} matching urls"
      work_repositories.each do |url|
        exitstatus = work_url(url)
        puts " " if @opts.verbose?
        return exitstatus if @opts[:stop] && exitstatus != 0
      end
      0
    end

    def work_url(url)
      return 0 if url == "https://gitlab.com/lou-parslow/raykit.git"

      puts Rainbow(url).yellow.bright if @opts.verbose?
      repo = Raykit::Git::Repository.new(url)
      work = Raykit::Git::Directory.new(repo.get_dev_dir("work"))
      unless Dir.exist?(work.directory)
        clone = Command.new("git clone #{url} #{work.directory} --recursive")
        puts clone.summary unless @opts.quiet?
        if clone.exitstatus != 0
          clone.details
          return clone.exitstatus
        end
      end
      if Dir.exist?(work.directory)
        Dir.chdir(work.directory) do
          if File.exist?("rakefile.rb")
            rake = Raykit::Command.new("rake #{@opts[:task]}")
            rake.summary(true) if !@opts.quiet? || rake.exitstatus != 0
            if rake.exitstatus != 0
              rake.details
              rake.summary true
              return rake.exitstatus
            end
          else
            puts("rakefile.rb not found in #{work.directory}") if @opts.verbose?
            return 0
          end
        end
      end
      0
    end

    def pull
      pattern = ""
      pattern = @opts.arguments[1] if @opts.arguments.length > 1
      REPOSITORIES.matches(pattern).each do |url|
        repo = Raykit::Git::Repository.new(url)
        work = Raykit::Git::Directory.new(repo.get_dev_dir("work"))
        if Dir.exist?(work.directory) && !work.directory == ".git"
          Dir.chdir(work.directory) do
            diff = `git diff`.strip
            status = `git status`.strip
            if diff.length.zero? && status.include?("nothing to commit")
              cmd = Command.new("git pull")
              cmd.summary if @opts.verbose?

              if cmd.exitstatus != 0
                cmd.details
                abort Rainbow(cmd.summary).blue.bright if @opts.quit?
              end
            end
          end
        end
      rescue StandardError => e
        issue = "error occurred for pull of #{url} #{e}"
        if @opts.quit?
          abort Rainbow(issue).blue.bright
        else
          puts Rainbow(issue).blue.bright
        end
      end
    end

    def import
      pattern = ""
      pattern = @opts.arguments[1] if @opts.arguments.length > 1
      puts "scanning..."
      count = REPOSITORIES.length
      REPOSITORIES.import(pattern)
      new_count = REPOSITORIES.length - count
      puts "imported #{new_count} git repositories"
    end

    def clean
      pattern = ""
      pattern = @opts.arguments[1] if @opts.arguments.length > 1
      REPOSITORIES.matches(pattern).each do |url|
        repo = Raykit::Git::Repository.new(url)
        work = Raykit::Git::Directory.new(repo.get_dev_dir("work"))
        next unless Dir.exist?(work.directory)

        puts "removing #{work.directory}"
        begin
          FileUtils.rm_rf(work.directory, secure: true)
        rescue StandardError
          puts "error removing #{work.directory}"
        end
      end
    end

    # def sync_version

    #    if(@opts.arguments.length < 3)

    #        puts 'source and destination arguments are required for sync_version'

    #        return 1

    #    end

    #    source = @opts.arguments[1]

    #    dest = @opts.arguments[2]

    #    Raykit::Version::sync_file_versions(source,dest)

    #    version = Raykit::Version::detect(source,false)

    #    puts "version #{version} detected in #{source}, updating #{dest} to match."

    # end


    def copy
      if @opts.arguments.length < 3
        puts "source and destination arguments are required for copy"
        return 1
      end
      source = @opts.arguments[1]
      dest = @opts.arguments[2]
      FileUtils.cp(source, dest)
      puts "copied #{source} to #{dest}"
    end

    def rake(hash)
      REPOSITORIES.each do |remote|
        next unless remote.include?(hash[:pattern])

        begin
          puts "remote: #{remote}"
          cmd = Raykit::Rake.run(remote, "master")
          elapsed_str = Timer.get_elapsed_str(cmd.elapsed)
          if cmd.exitstatus.zero?
            puts "#{elapsed_str} #{Rainbow(SECRETS.hide(cmd.command)).yellow.bright} (#{cmd.directory})"
          else
            puts "\r\n#{cmd.command}\r\n"
            puts "\r\n#{cmd.output}\r\n"
            puts "\r\n#{cmd.error}\r\n"
            puts ""
            puts "#{Rainbow(elapsed_str).red.bright} #{Rainbow(cmd.command).white}"
          end
        rescue StandardError
          puts "rescued..."
        end
      end
    end
  end
end

# CONSOLE=Raykit::Console.new