clarkda/cocoapods-repo-hg

View on GitHub
lib/pod/command/repo_hg.rb

Summary

Maintainability
B
4 hrs
Test Coverage
require 'fileutils'

module Pod
  class Command
    class RepoHg < Command
      self.abstract_command = true

      self.summary = 'Manage spec-repositories using mercurial'

      class Add < RepoHg
        self.summary = 'Add a spec-repo using hg.'

        self.description = <<-DESC
          Check out `URL` in the local spec-repos directory at `~/.cocoapods/repos/`. The
          remote can later be referred to by `NAME`.
        DESC

        self.arguments = [
            CLAide::Argument.new('URL', true),
            CLAide::Argument.new('NAME', true)
        ]

        def initialize(argv)
          @name, @url = argv.shift_argument, argv.shift_argument
          super
        end

        def validate!
          super
          unless @name && @url
            help! "Adding a spec-repo needs a `NAME` and a `URL`."
          end
        end

        def run
          UI.section("Cloning spec-repo `#{@name}` from `#{@url}` using hg") do
            config.repos_dir.mkpath
            Dir.chdir(config.repos_dir) do
              command = "clone '#{@url}' #{@name}"
              !hg(command)
            end
            SourcesManager.check_version_information(dir) #todo: TEST ME
          end
        end
      end

      #-----------------------------------------------------------------------#

      class Update < RepoHg
        self.summary = 'Update a hg spec-repo.'

        self.description = <<-DESC
          Updates the checked out spec-repo `NAME`.
        DESC

        self.arguments = [
            CLAide::Argument.new('NAME', true)
        ]

        def initialize(argv)
          @name = argv.shift_argument
          super
        end

        def validate!
          super
          unless @name
            help! "Updating a spec-repo needs a `NAME`."
          end
        end

        def run
          update(@name, true) #todo: dusty
        end

        #@!group Update helpers
        #-----------------------------------------------------------------------#

        private

        # Slightly modified SourcesManager->update to deal with mercurial updates.
        #
        # Original contributors:
        #
        # Fabio Pelosin   http://github.com/irrationalfab
        # Boris Bügling    http://githun.com/neonichu
        #

        # Updates the local copy of the spec-repo with the given name
        #
        # @param  [String] source_name name
        #
        # @return [void]
        #
        def update(source_name = nil, show_output = false)
          if source_name
            sources = [hg_source_named(source_name)]
          else
            sources =  hg_sources
          end
          sources.each do |source|
            UI.section "Updating spec repo `#{source.name}`" do
              Dir.chdir(source.repo) do
                begin
                  output = hg!('pull')
                  UI.puts output if show_output && !config.verbose?
                rescue Informative => e
                  UI.warn 'CocoaPods was not able to update the ' \
                  "`#{source.name}` repo. If this is an unexpected issue " \
                  'and persists you can inspect it running ' \
                  '`pod repo-hg update --verbose`'
                end
              end
              SourcesManager.check_version_information(source.repo)
            end
          end
        end

        # @return [Source] The hg source with the given name. If no hg source
        #         with given name is found it raises.
        #
        # @param  [String] name
        #         The name of the source.
        #
        def hg_source_named(name)
          specified_source = SourcesManager.aggregate.sources.find { |s| s.name == name }
          unless specified_source
            raise Informative, "Unable to find the `#{name}` repo."
          end
          unless hg_repo?(specified_source.repo)
            raise Informative, "The `#{name}` repo is not a hg repo."
          end
          specified_source
        end

        # @return [Source] The list of the hg sources.
        #
        def hg_sources
          SourcesManager.all.select do |source|
            hg_repo?(source.repo)
          end
        end

        # Returns whether a source is a HG repo.
        #
        # @param  [Pathname] dir
        #         The directory where the source is stored.
        #
        # @return [Bool] Whether the given source is a HG repo.
        #
        def hg_repo?(dir)
          Dir.chdir(dir) { hg('status  >/dev/null 2>&1') }
          $?.success?
        end
      end

      #-----------------------------------------------------------------------#

      # ~Verbatim Repo->Lint
      #
      # Original contributors:
      #
      # Fabio Pelosin   http://github.com/irrationalfab
      # Eloy Durán      http://githun.com/alloy

      # Repo validation can probably be pulled out and stuck into core, pretty generic

      #todo: lint is blowing up, fix it - dusty

      class Lint < RepoHg
        self.summary = 'Validates all specs in a repo.'

        self.description = <<-DESC
          Lints the spec-repo `NAME`. If a directory is provided it is assumed
          to be the root of a repo. Finally, if `NAME` is not provided this
          will lint all the spec-repos known to CocoaPods.
        DESC

        self.arguments = [
            CLAide::Argument.new(%w(NAME DIRECTORY), true)
        ]

        def self.options
          [["--only-errors", "Lint presents only the errors"]].concat(super)
        end

        def initialize(argv)
          @name = argv.shift_argument
          @only_errors = argv.flag?('only-errors')
          super
        end

        # @todo Part of this logic needs to be ported to cocoapods-core so web
        #       services can validate the repo.
        #
        # @todo add UI.print and enable print statements again.
        #
        def run
          if @name
            dirs = File.exists?(@name) ? [ Pathname.new(@name) ] : [ dir ]
          else
            dirs = config.repos_dir.children.select {|c| c.directory?}
          end
          dirs.each do |dir|
            SourcesManager.check_version_information(dir) #todo: test me
            UI.puts "\nLinting spec repo `#{dir.realpath.basename}`\n".yellow

            validator = Source::HealthReporter.new(dir)
            validator.pre_check do |name, version|
              UI.print '.'
            end
            report = validator.analyze
            UI.puts
            UI.puts

            report.pods_by_warning.each do |message, versions_by_name|
              UI.puts "-> #{message}".yellow
              versions_by_name.each { |name, versions| UI.puts "  - #{name} (#{versions * ', '})" }
              UI.puts
            end

            report.pods_by_error.each do |message, versions_by_name|
              UI.puts "-> #{message}".red
              versions_by_name.each { |name, versions| UI.puts "  - #{name} (#{versions * ', '})" }
              UI.puts
            end

            UI.puts "Analyzed #{report.analyzed_paths.count} podspecs files.\n\n"
            if report.pods_by_error.count.zero?
              UI.puts "All the specs passed validation.".green << "\n\n"
            else
              raise Informative, "#{report.pods_by_error.count} podspecs failed validation."
            end
          end
        end
      end

      #-----------------------------------------------------------------------#

      # ~Verbatim Repo->Remove
      #
      # Original contributors:
      #
      # Joshua Kalpin   https://github.com/Kapin
      # Kyle Fuller     https://github.com/kylef

      # Repo removal should probably be pulled out, also pretty generic

      class Remove < RepoHg
        self.summary = 'Remove a spec repo'

        self.description = <<-DESC
          Deletes the checked out copy named `NAME` from the local spec-repos directory at `~/.cocoapods/repos/.`
        DESC

        self.arguments = [
            CLAide::Argument.new('NAME', true)
        ]

        def initialize(argv)
          @name = argv.shift_argument
          super
        end

        def validate!
          super
          help! 'Deleting a repo needs a `NAME`.' unless @name
          help! "repo #{@name} does not exist" unless File.directory?(dir)
          help! "You do not have permission to delete the #{@name} repository." \
                "Perhaps try prefixing this command with sudo." unless File.writable?(dir)
        end

        def run
          UI.section("Removing spec repo `#{@name}`") do
            FileUtils.rm_rf(dir)
          end
        end
      end

      extend Executable
      executable :hg

      def dir
        config.repos_dir + @name
      end
    end
  end
end