[devdocsgjs/main: 582/1867] Create command to check for updates




commit d0300dd5851bcacbcba2c0ddacd057ada57b6cc2
Author: Jasper van Merle <jaspervmerle gmail com>
Date:   Fri Mar 8 03:23:40 2019 +0100

    Create command to check for updates

 lib/docs/core/scraper.rb | 29 ++++++++++++++++++
 lib/tasks/updates.thor   | 78 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 107 insertions(+)
---
diff --git a/lib/docs/core/scraper.rb b/lib/docs/core/scraper.rb
index 083b0015..89191e48 100644
--- a/lib/docs/core/scraper.rb
+++ b/lib/docs/core/scraper.rb
@@ -132,6 +132,35 @@ module Docs
       end
     end
 
+    def get_latest_version
+      raise NotImplementedError
+    end
+
+    # Returns whether or not this scraper is outdated.
+    #
+    # The default implementation assumes the documentation uses a semver(-like) approach when it comes to 
versions.
+    # Patch updates are ignored because there are usually little to no documentation changes in bug-fix-only 
releases.
+    #
+    # Scrapers of documentations that do not use this versioning approach should override this method.
+    #
+    # Examples of the default implementation:
+    # 1 -> 2 = outdated
+    # 1.1 -> 1.2 = outdated
+    # 1.1.1 -> 1.1.2 = not outdated
+    def is_outdated(current_version, latest_version)
+      current_parts = current_version.split(/\./).map(&:to_i)
+      latest_parts = latest_version.split(/\./).map(&:to_i)
+
+      # Only check the first two parts, the third part is for patch updates
+      [0, 1].each do |i|
+        break if i >= current_parts.length or i >= latest_parts.length
+        return true if latest_parts[i] > current_parts[i]
+        return false if latest_parts[i] < current_parts[i]
+      end
+
+      false
+    end
+
     private
 
     def request_one(url)
diff --git a/lib/tasks/updates.thor b/lib/tasks/updates.thor
new file mode 100644
index 00000000..35d52e28
--- /dev/null
+++ b/lib/tasks/updates.thor
@@ -0,0 +1,78 @@
+class UpdatesCLI < Thor
+  def self.to_s
+    'Updates'
+  end
+
+  def initialize(*args)
+    require 'docs'
+    require 'progress_bar'
+    super
+  end
+
+  desc 'check [doc]...', 'Check for outdated documentations'
+  def check(*names)
+    # Convert names to a list of Scraper instances
+    # Versions are omitted, if v10 is outdated than v8 is aswell
+    docs = names.map {|name| Docs.find(name.split(/@|~/)[0], false)}.uniq
+
+    # Check all documentations for updates when no arguments are given
+    docs = Docs.all if docs.empty?
+
+    progress_bar = ::ProgressBar.new docs.length
+    progress_bar.write
+
+    results = docs.map do |doc|
+      result = check_doc(doc)
+      progress_bar.increment!
+      result
+    end
+
+    outdated = results.select {|result| result.is_a?(Hash) && result[:is_outdated]}
+    return if outdated.empty?
+
+    logger.info("Outdated documentations (#{outdated.length}):")
+    outdated.each do |result|
+      logger.info("#{result[:name]}: #{result[:current_version]} -> #{result[:latest_version]}")
+    end
+  rescue Docs::DocNotFound => error
+    logger.error(error)
+    logger.info('Run "thor docs:list" to see the list of docs.')
+  end
+
+  private
+
+  def check_doc(doc)
+    # Scraper versions are always sorted from new to old
+    # Therefore, the first item's release value is the latest current scraper version
+    #
+    # For example, a scraper could scrape 3 versions: 10, 11 and 12
+    # doc.versions.first would be the scraper for version 12 if the scraper is written like all the other 
scrapers are
+    instance = doc.versions.first.new
+
+    current_version = instance.options[:release]
+    return nil if current_version.nil?
+
+    latest_version = instance.get_latest_version
+    return nil if latest_version.nil?
+
+    {
+      name: doc.name,
+      current_version: current_version,
+      latest_version: latest_version,
+      is_outdated: instance.is_outdated(current_version, latest_version)
+    }
+  rescue NotImplementedError
+    logger.warn("Can't check #{doc.name}, get_latest_version is not implemented")
+  rescue => error
+    logger.error("Error while checking #{doc.name}: #{error}")
+  end
+
+  def logger
+    @logger ||= Logger.new($stdout).tap do |logger|
+      logger.formatter = proc do |severity, datetime, progname, msg|
+        prefix = severity != "INFO" ? "[#{severity}] " : ""
+        "#{prefix}#{msg}\n"
+      end
+    end
+  end
+end


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]