Finish get_latest_version for 81 scrapers and add uploading functionality

pull/986/head
Jasper van Merle 6 years ago
parent 2d1e8aa00c
commit 3dc17a9b29

@ -40,6 +40,7 @@ group :docs do
gem 'unix_utils', require: false gem 'unix_utils', require: false
gem 'tty-pager', require: false gem 'tty-pager', require: false
gem 'net-sftp', '>= 2.1.3.rc2', require: false gem 'net-sftp', '>= 2.1.3.rc2', require: false
gem 'terminal-table', require: false
end end
group :test do group :test do

@ -101,6 +101,8 @@ GEM
unicode-display_width (~> 1.4.0) unicode-display_width (~> 1.4.0)
unicode_utils (~> 1.4.0) unicode_utils (~> 1.4.0)
strings-ansi (0.1.0) strings-ansi (0.1.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thin (1.7.2) thin (1.7.2)
daemons (~> 1.0, >= 1.0.9) daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4) eventmachine (~> 1.0, >= 1.0.4)
@ -153,6 +155,7 @@ DEPENDENCIES
sinatra-contrib sinatra-contrib
sprockets sprockets
sprockets-helpers sprockets-helpers
terminal-table
thin thin
thor thor
tty-pager tty-pager

@ -132,7 +132,7 @@ module Docs
end end
end end
def get_latest_version(&block) def get_latest_version(options, &block)
raise NotImplementedError raise NotImplementedError
end end
@ -147,15 +147,15 @@ module Docs
# 1 -> 2 = outdated # 1 -> 2 = outdated
# 1.1 -> 1.2 = outdated # 1.1 -> 1.2 = outdated
# 1.1.1 -> 1.1.2 = not outdated # 1.1.1 -> 1.1.2 = not outdated
def is_outdated(current_version, latest_version) def is_outdated(scraper_version, latest_version)
current_parts = current_version.split(/\./).map(&:to_i) scraper_parts = scraper_version.split(/\./).map(&:to_i)
latest_parts = latest_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 # Only check the first two parts, the third part is for patch updates
[0, 1].each do |i| [0, 1].each do |i|
break if i >= current_parts.length or i >= latest_parts.length break if i >= scraper_parts.length or i >= latest_parts.length
return true if latest_parts[i] > current_parts[i] return true if latest_parts[i] > scraper_parts[i]
return false if latest_parts[i] < current_parts[i] return false if latest_parts[i] < scraper_parts[i]
end end
false false
@ -231,38 +231,62 @@ module Docs
{} {}
end end
#
# Utility methods for get_latest_version # Utility methods for get_latest_version
#
def fetch(url, &block) def fetch(url, options, &block)
Request.run(url) do |response| headers = {}
if options.key?(:github_token) and url.start_with?('https://api.github.com/')
headers['Authorization'] = "token #{options[:github_token]}"
end
options[:logger].debug("Fetching #{url}")
Request.run(url, { headers: headers }) do |response|
if response.success? if response.success?
block.call response.body block.call response.body
else else
options[:logger].error("Couldn't fetch #{url} (response code #{response.code})")
block.call nil block.call nil
end end
end end
end end
def fetch_doc(url, &block) def fetch_doc(url, options, &block)
fetch(url) do |body| fetch(url, options) do |body|
parser = Parser.new(body) block.call Nokogiri::HTML.parse body, nil, 'UTF-8'
block.call parser.html
end end
end end
def fetch_json(url, &block) def fetch_json(url, options, &block)
fetch(url) do |body| fetch(url, options) do |body|
json = JSON.parse(body) json = JSON.parse(body)
block.call json block.call json
end end
end end
def get_npm_version(package, &block) def get_npm_version(package, options, &block)
fetch_json("https://registry.npmjs.com/#{package}") do |json| fetch_json("https://registry.npmjs.com/#{package}", options) do |json|
block.call json['dist-tags']['latest'] block.call json['dist-tags']['latest']
end end
end end
def get_latest_github_release(owner, repo, options, &block)
fetch_json("https://api.github.com/repos/#{owner}/#{repo}/releases/latest", options, &block)
end
def get_github_tags(owner, repo, options, &block)
fetch_json("https://api.github.com/repos/#{owner}/#{repo}/tags", options, &block)
end
def get_github_file_contents(owner, repo, path, options, &block)
fetch_json("https://api.github.com/repos/#{owner}/#{repo}/contents/#{path}", options) do |json|
block.call(Base64.decode64(json['content']))
end
end
module FixInternalUrlsBehavior module FixInternalUrlsBehavior
def self.included(base) def self.included(base)
base.extend ClassMethods base.extend ClassMethods

@ -155,8 +155,8 @@ module Docs
end end
end end
def get_latest_version(&block) def get_latest_version(options, &block)
get_npm_version('@angular/core', &block) get_npm_version('@angular/core', options, &block)
end end
private private

@ -70,8 +70,8 @@ module Docs
self.base_url = "https://code.angularjs.org/#{release}/docs/partials/" self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
end end
def get_latest_version(&block) def get_latest_version(options, &block)
get_npm_version('angular', &block) get_npm_version('angular', options, &block)
end end
end end
end end

@ -88,8 +88,8 @@ module Docs
list_of_all_modules.html) list_of_all_modules.html)
end end
def get_latest_version(&block) def get_latest_version(options, &block)
fetch_doc('https://docs.ansible.com/ansible/latest/index.html') do |doc| fetch_doc('https://docs.ansible.com/ansible/latest/index.html', options) do |doc|
block.call doc.at_css('.DocSiteProduct-CurrentVersion').content.strip block.call doc.at_css('.DocSiteProduct-CurrentVersion').content.strip
end end
end end

@ -34,8 +34,8 @@ module Docs
Licensed under the Apache License, Version 2.0. Licensed under the Apache License, Version 2.0.
HTML HTML
def get_latest_version(&block) def get_latest_version(options, &block)
fetch_doc('http://httpd.apache.org/docs/') do |doc| fetch_doc('http://httpd.apache.org/docs/', options) do |doc|
block.call doc.at_css('#apcontents > ul a')['href'][0...-1] block.call doc.at_css('#apcontents > ul a')['href'][0...-1]
end end
end end

@ -43,8 +43,8 @@ module Docs
self.base_url = "https://pig.apache.org/docs/r#{release}/" self.base_url = "https://pig.apache.org/docs/r#{release}/"
end end
def get_latest_version(&block) def get_latest_version(options, &block)
fetch_doc('https://pig.apache.org/') do |doc| fetch_doc('https://pig.apache.org/', options) do |doc|
item = doc.at_css('div[id="menu_1.2"] > .menuitem:last-child') item = doc.at_css('div[id="menu_1.2"] > .menuitem:last-child')
block.call item.content.strip.sub(/Release /, '') block.call item.content.strip.sub(/Release /, '')
end end

@ -18,8 +18,8 @@ module Docs
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(&block) def get_latest_version(options, &block)
fetch_doc('https://caolan.github.io/async/') do |doc| fetch_doc('https://caolan.github.io/async/', options) do |doc|
version = doc.at_css('#version-dropdown > a').content.strip[1..-1] version = doc.at_css('#version-dropdown > a').content.strip[1..-1]
block.call version block.call version
end end

@ -23,8 +23,8 @@ module Docs
'<div></div>' '<div></div>'
end end
def get_latest_version(&block) def get_latest_version(options, &block)
fetch_doc('https://babeljs.io/docs/en/') do |doc| fetch_doc('https://babeljs.io/docs/en/', options) do |doc|
block.call doc.at_css('a[href="/versions"] > h3').content block.call doc.at_css('a[href="/versions"] > h3').content
end end
end end

@ -21,8 +21,8 @@ module Docs
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(&block) def get_latest_version(options, &block)
fetch_doc('https://backbonejs.org/') do |doc| fetch_doc('https://backbonejs.org/', options) do |doc|
version = doc.at_css('.version').content version = doc.at_css('.version').content
block.call version[1...-1] block.call version[1...-1]
end end

@ -18,8 +18,8 @@ module Docs
Licensed under the GNU Free Documentation License. Licensed under the GNU Free Documentation License.
HTML HTML
def get_latest_version(&block) def get_latest_version(options, &block)
fetch('https://www.gnu.org/software/bash/manual/html_node/index.html') do |body| fetch('https://www.gnu.org/software/bash/manual/html_node/index.html', options) do |body|
version = body.scan(/, Version ([0-9.]+)/)[0][0] version = body.scan(/, Version ([0-9.]+)/)[0][0]
block.call version[0...-1] block.call version[0...-1]
end end

@ -19,8 +19,8 @@ module Docs
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(&block) def get_latest_version(options, &block)
get_npm_version('bluebird', &block) get_npm_version('bluebird', options, &block)
end end
end end
end end

@ -34,5 +34,11 @@ module Docs
options[:only] = %w(getting-started/ css/ components/ javascript/) options[:only] = %w(getting-started/ css/ components/ javascript/)
end end
def get_latest_version(options, &block)
fetch_doc('https://getbootstrap.com/', options) do |doc|
block.call doc.at_css('#bd-versions').content.strip[1..-1]
end
end
end end
end end

@ -27,5 +27,12 @@ module Docs
self.release = '0.11.7' self.release = '0.11.7'
self.base_url = "https://bottlepy.org/docs/#{self.version}/" self.base_url = "https://bottlepy.org/docs/#{self.version}/"
end end
def get_latest_version(options, &block)
fetch_doc('https://bottlepy.org/docs/stable/', options) do |doc|
label = doc.at_css('.sphinxsidebarwrapper > ul > li > b')
block.call label.content.sub(/Bottle /, '')
end
end
end end
end end

@ -19,5 +19,9 @@ module Docs
&copy; 2018 Bower contributors<br> &copy; 2018 Bower contributors<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('bower', options, &block)
end
end end
end end

@ -71,6 +71,12 @@ module Docs
self.base_url = 'https://api.cakephp.org/2.7/' self.base_url = 'https://api.cakephp.org/2.7/'
end end
def get_latest_version(options, &block)
fetch_doc('https://api.cakephp.org/3.7/', options) do |doc|
block.call doc.at_css('.version-picker .dropdown-toggle').content.strip
end
end
private private
def parse(response) def parse(response)

@ -23,5 +23,9 @@ module Docs
&copy; 2016 Chai.js Assertion Library<br> &copy; 2016 Chai.js Assertion Library<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('chai', options, &block)
end
end end
end end

@ -47,5 +47,12 @@ module Docs
options[:only_patterns] = [/\A#{client_path}\//, /\A#{server_path}\//] options[:only_patterns] = [/\A#{client_path}\//, /\A#{server_path}\//]
end end
def get_latest_version(options, &block)
fetch_doc('https://docs-archive.chef.io/', options) do |doc|
cell = doc.at_css('.main-archives > tr:nth-child(2) > td:nth-child(2)')
block.call cell.content.sub(/Chef Client /, '')
end
end
end end
end end

@ -27,5 +27,11 @@ module Docs
self.release = '1.7' self.release = '1.7'
self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.7.0/' self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.7.0/'
end end
def get_latest_version(options, &block)
fetch_doc('http://clojure.github.io/clojure/index.html', options) do |doc|
block.call doc.at_css('#header-version').content[1..-1]
end
end
end end
end end

@ -59,5 +59,12 @@ module Docs
self.release = '3.5.2' self.release = '3.5.2'
self.base_url = 'https://cmake.org/cmake/help/v3.5/' self.base_url = 'https://cmake.org/cmake/help/v3.5/'
end end
def get_latest_version(options, &block)
fetch_doc('https://cmake.org/documentation/', options) do |doc|
link = doc.at_css('.entry-content ul > li > strong > a > big')
block.call link.content.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -18,5 +18,11 @@ module Docs
&copy; 2011 Michael Bodnarchuk and contributors<br> &copy; 2011 Michael Bodnarchuk and contributors<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://codeception.com/changelog', options) do |doc|
block.call doc.at_css('#page > h4').content
end
end
end end
end end

@ -21,5 +21,9 @@ module Docs
&copy; 2015 DavertMik &lt;davert@codegyre.com&gt; (http://codegyre.com)<br> &copy; 2015 DavertMik &lt;davert@codegyre.com&gt; (http://codegyre.com)<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('codeceptjs', options, &block)
end
end end
end end

@ -38,5 +38,12 @@ module Docs
version '3' do version '3' do
self.release = '3.1.8' self.release = '3.1.8'
end end
def get_latest_version(options, &block)
fetch_doc('https://codeigniter.com/user_guide/changelog.html', options) do |doc|
header = doc.at_css('#change-log h2')
block.call header.content.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -30,5 +30,9 @@ module Docs
options[:container] = '.container' options[:container] = '.container'
end end
def get_latest_version(options, &block)
get_npm_version('coffeescript', options, &block)
end
end end
end end

@ -42,5 +42,14 @@ module Docs
self.release = '6.5.0' self.release = '6.5.0'
self.base_url = 'https://cordova.apache.org/docs/en/6.x/' self.base_url = 'https://cordova.apache.org/docs/en/6.x/'
end end
def get_latest_version(options, &block)
fetch_doc('https://cordova.apache.org/docs/en/latest/', options) do |doc|
label = doc.at_css('#versionDropdown').content.strip
version = label.scan(/([0-9.]+)/)[0][0]
version = version[0...-1] if version.end_with?('.')
block.call version
end
end
end end
end end

@ -34,5 +34,11 @@ module Docs
HTML HTML
end end
} }
def get_latest_version(options, &block)
fetch('https://crystal-lang.org/api', options) do |body|
block.call body.scan(/Crystal Docs ([0-9.]+)/)[0][0]
end
end
end end
end end

@ -26,5 +26,11 @@ module Docs
def initial_urls def initial_urls
%w(https://dlang.org/phobos/index.html https://dlang.org/spec/intro.html) %w(https://dlang.org/phobos/index.html https://dlang.org/spec/intro.html)
end end
def get_latest_version(options, &block)
fetch_doc('https://dlang.org/changelog/', options) do |doc|
block.call doc.at_css('#content > ul > li:nth-child(2) > a')['id']
end
end
end end
end end

@ -58,5 +58,9 @@ module Docs
options[:root_title] = 'D3.js' options[:root_title] = 'D3.js'
options[:only_patterns] = [/\.md\z/] options[:only_patterns] = [/\.md\z/]
end end
def get_latest_version(options, &block)
get_npm_version('d3', options, &block)
end
end end
end end

@ -31,5 +31,12 @@ module Docs
self.release = '1.24.3' self.release = '1.24.3'
self.base_url = "https://api.dartlang.org/stable/#{release}/" self.base_url = "https://api.dartlang.org/stable/#{release}/"
end end
def get_latest_version(options, &block)
fetch_doc('https://api.dartlang.org/', options) do |doc|
label = doc.at_css('footer > span').content.strip
block.call label.sub(/Dart /, '')
end
end
end end
end end

@ -63,5 +63,11 @@ module Docs
self.release = '1.8.18' self.release = '1.8.18'
self.base_url = 'https://docs.djangoproject.com/en/1.8/' self.base_url = 'https://docs.djangoproject.com/en/1.8/'
end end
def get_latest_version(options, &block)
fetch_doc('https://docs.djangoproject.com/', options) do |doc|
block.call doc.at_css('#doc-versions > li.current > span > strong').content
end
end
end end
end end

@ -137,5 +137,12 @@ module Docs
options[:container] = '#docs' options[:container] = '#docs'
options[:only_patterns] << /\Aswarm\// options[:only_patterns] << /\Aswarm\//
end end
def get_latest_version(options, &block)
fetch_doc('https://docs.docker.com/', options) do |doc|
label = doc.at_css('.nav-container button.dropdown-toggle').content.strip
block.call label.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -36,6 +36,12 @@ module Docs
urls.map { |url| "<a href='#{url}'>#{url}</a>" }.join urls.map { |url| "<a href='#{url}'>#{url}</a>" }.join
end end
def get_latest_version(options, &block)
fetch_doc('https://dojotoolkit.org/api/', options) do |doc|
block.call doc.at_css('#versionSelector > option[selected]').content
end
end
private private
def get_url_list(json, set = Set.new) def get_url_list(json, set = Set.new)

@ -98,5 +98,14 @@ module Docs
/\A[\w\-\.]+\.php\/7\.x\z/ /\A[\w\-\.]+\.php\/7\.x\z/
] ]
end end
def get_latest_version(options, &block)
fetch_doc('http://cgit.drupalcode.org/drupal', options) do |doc|
version = doc.at_css('td.form > form > select > option[selected]').content
version = version.scan(/([0-9.]+)/)[0][0]
version = version[0...-1] if version.end_with?('.')
block.call version
end
end
end end
end end

@ -22,5 +22,11 @@ module Docs
&copy; 2013&ndash;2018 GitHub Inc.<br> &copy; 2013&ndash;2018 GitHub Inc.<br>
Licensed under the MIT license. Licensed under the MIT license.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://electronjs.org/docs', options) do |doc|
block.call doc.at_css('.docs-version').content
end
end
end end
end end

@ -97,5 +97,11 @@ module Docs
'https://elixir-lang.org/getting-started/' 'https://elixir-lang.org/getting-started/'
] ]
end end
def get_latest_version(options, &block)
fetch_doc('https://hexdocs.pm/elixir/api-reference.html', options) do |doc|
block.call doc.at_css('h2.sidebar-projectVersion').content.strip[1..-1]
end
end
end end
end end

@ -56,5 +56,11 @@ module Docs
https://emberjs.com/api/ember-data/2.14/classes/DS https://emberjs.com/api/ember-data/2.14/classes/DS
) )
end end
def get_latest_version(options, &block)
fetch_doc('https://emberjs.com/api/ember/release', options) do |doc|
block.call doc.at_css('.sidebar > .select-container .ember-power-select-selected-item').content.strip
end
end
end end
end end

@ -55,5 +55,11 @@ module Docs
version '18' do version '18' do
self.release = '18.3' self.release = '18.3'
end end
def get_latest_version(options, &block)
fetch_doc('https://www.erlang.org/downloads', options) do |doc|
block.call doc.at_css('.col-lg-3 > ul > li').content.strip
end
end
end end
end end

@ -20,5 +20,9 @@ module Docs
&copy; JS Foundation and other contributors<br> &copy; JS Foundation and other contributors<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('eslint', options, &block)
end
end end
end end

@ -28,5 +28,9 @@ module Docs
&copy; 2017 StrongLoop, IBM, and other expressjs.com contributors.<br> &copy; 2017 StrongLoop, IBM, and other expressjs.com contributors.<br>
Licensed under the Creative Commons Attribution-ShareAlike License v3.0. Licensed under the Creative Commons Attribution-ShareAlike License v3.0.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('express', options, &block)
end
end end
end end

@ -33,5 +33,11 @@ module Docs
self.release = '1.2.0' self.release = '1.2.0'
self.base_url = "https://falcon.readthedocs.io/en/#{self.release}/" self.base_url = "https://falcon.readthedocs.io/en/#{self.release}/"
end end
def get_latest_version(options, &block)
fetch_doc('https://falcon.readthedocs.io/en/stable/changes/index.html', options) do |doc|
block.call doc.at_css('#changelogs ul > li > a').content
end
end
end end
end end

@ -46,5 +46,11 @@ module Docs
self.release = '2.2.0' self.release = '2.2.0'
self.base_url = "https://fishshell.com/docs/#{version}/" self.base_url = "https://fishshell.com/docs/#{version}/"
end end
def get_latest_version(options, &block)
fetch_doc('http://fishshell.com/docs/current/index.html', options) do |doc|
block.call doc.at_css('#toc-index').content.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -18,5 +18,9 @@ module Docs
&copy; 2013&ndash;present Facebook Inc.<br> &copy; 2013&ndash;present Facebook Inc.<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('flow-bin', options, &block)
end
end end
end end

@ -19,5 +19,11 @@ module Docs
&copy; 2005&ndash;2018 Linus Torvalds and others<br> &copy; 2005&ndash;2018 Linus Torvalds and others<br>
Licensed under the GNU General Public License version 2. Licensed under the GNU General Public License version 2.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://git-scm.com/', options) do |doc|
block.call doc.at_css('.version').content.strip
end
end
end end
end end

@ -99,5 +99,12 @@ module Docs
options[:replace_paths] = CPP_PATHS options[:replace_paths] = CPP_PATHS
end end
def get_latest_version(options, &block)
fetch_doc('https://gcc.gnu.org/onlinedocs/', options) do |doc|
label = doc.at_css('ul > li > ul > li > a').content.strip
block.call label.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -25,5 +25,12 @@ module Docs
self.release = '4.9.3' self.release = '4.9.3'
self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/" self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/"
end end
def get_latest_version(options, &block)
fetch_doc('https://gcc.gnu.org/onlinedocs/', options) do |doc|
label = doc.at_css('ul > li > ul > li > a').content.strip
block.call label.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -24,6 +24,15 @@ module Docs
Licensed under the Creative Commons Attribution License 3.0. Licensed under the Creative Commons Attribution License 3.0.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://golang.org/pkg/', options) do |doc|
footer = doc.at_css('#footer').content
version = footer.scan(/go([0-9.]+)/)[0][0]
version = version[0...-1] if version.end_with?('.')
block.call version
end
end
private private
def parse(response) # Hook here because Nokogori removes whitespace from textareas def parse(response) # Hook here because Nokogori removes whitespace from textareas

@ -37,5 +37,11 @@ module Docs
self.release = '2.1' self.release = '2.1'
self.base_url = "http://docs.godotengine.org/en/#{self.version}/" self.base_url = "http://docs.godotengine.org/en/#{self.version}/"
end end
def get_latest_version(options, &block)
fetch_doc('https://docs.godotengine.org/', options) do |doc|
block.call doc.at_css('.version').content.strip
end
end
end end
end end

@ -17,5 +17,11 @@ module Docs
&copy; 2011&ndash;2016 The Graphite Project<br> &copy; 2011&ndash;2016 The Graphite Project<br>
Licensed under the Apache License, Version 2.0. Licensed under the Apache License, Version 2.0.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://graphite.readthedocs.io/en/latest/releases.html', options) do |doc|
block.call doc.at_css('#release-notes li > a').content
end
end
end end
end end

@ -26,5 +26,9 @@ module Docs
&copy; GruntJS Team<br> &copy; GruntJS Team<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('grunt-cli', options, &block)
end
end end
end end

@ -19,5 +19,9 @@ module Docs
&copy; 2011&ndash;2017 by Yehuda Katz<br> &copy; 2011&ndash;2017 by Yehuda Katz<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('handlebars', options, &block)
end
end end
end end

@ -68,5 +68,12 @@ module Docs
options[:only_patterns] = [/\Alibraries\//] options[:only_patterns] = [/\Alibraries\//]
end end
def get_latest_version(options, &block)
fetch_doc('https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/', options) do |doc|
label = doc.at_css('.related > ul > li:last-child').content
block.call label.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -66,5 +66,12 @@ module Docs
version 'Python' do version 'Python' do
self.base_url = 'https://api.haxe.org/python/' self.base_url = 'https://api.haxe.org/python/'
end end
def get_latest_version(options, &block)
fetch_doc('https://api.haxe.org/', options) do |doc|
label = doc.at_css('.container.main-content h1 > small').content
block.call label.sub(/version /, '')
end
end
end end
end end

@ -19,5 +19,11 @@ module Docs
&copy; 2009&ndash;present Homebrew contributors<br> &copy; 2009&ndash;present Homebrew contributors<br>
Licensed under the BSD 2-Clause License. Licensed under the BSD 2-Clause License.
HTML HTML
def get_latest_version(options, &block)
get_latest_github_release('Homebrew', 'brew', options) do |release|
block.call release['name']
end
end
end end
end end

@ -54,5 +54,9 @@ module Docs
JS JS
capybara.html capybara.html
end end
def get_latest_version(options, &block)
get_npm_version('immutable', options, &block)
end
end end
end end

@ -46,5 +46,12 @@ module Docs
&copy; 2015 InfluxData, Inc.<br> &copy; 2015 InfluxData, Inc.<br>
Licensed under the MIT license. Licensed under the MIT license.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://docs.influxdata.com/influxdb/', options) do |doc|
label = doc.at_css('.navbar--current-product').content.strip
block.call label.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -17,5 +17,11 @@ module Docs
&copy; 2008&ndash;2017 Pivotal Labs<br> &copy; 2008&ndash;2017 Pivotal Labs<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_latest_github_release('jasmine', 'jasmine', options) do |release|
block.call release['name']
end
end
end end
end end

@ -28,5 +28,11 @@ module Docs
&copy; 2008&ndash;2018 Tom Preston-Werner and Jekyll contributors<br> &copy; 2008&ndash;2018 Tom Preston-Werner and Jekyll contributors<br>
Licensed under the MIT license. Licensed under the MIT license.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://jekyllrb.com/docs/', options) do |doc|
block.call doc.at_css('.meta a').content[1..-1]
end
end
end end
end end

@ -17,5 +17,11 @@ module Docs
&copy; 2014&ndash;present Facebook Inc.<br> &copy; 2014&ndash;present Facebook Inc.<br>
Licensed under the BSD License. Licensed under the BSD License.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://jestjs.io/docs/en/getting-started', options) do |doc|
block.call doc.at_css('header > a > h3').content
end
end
end end
end end

@ -22,5 +22,9 @@ module Docs
/Selectors\/odd/i, /Selectors\/odd/i,
/index/i /index/i
] ]
def get_latest_version(options, &block)
get_npm_version('jquery', options, &block)
end
end end
end end

@ -16,5 +16,12 @@ module Docs
options[:fix_urls] = ->(url) do options[:fix_urls] = ->(url) do
url.sub! 'http://api.jquerymobile.com/', 'https://api.jquerymobile.com/' url.sub! 'http://api.jquerymobile.com/', 'https://api.jquerymobile.com/'
end end
def get_latest_version(options, &block)
fetch_doc('https://jquerymobile.com/', options) do |doc|
label = doc.at_css('.download-box > .download-option:last-child > span').content
block.call label.sub(/Version /, '')
end
end
end end
end end

@ -15,5 +15,9 @@ module Docs
options[:fix_urls] = ->(url) do options[:fix_urls] = ->(url) do
url.sub! 'http://api.jqueryui.com/', 'https://api.jqueryui.com/' url.sub! 'http://api.jqueryui.com/', 'https://api.jqueryui.com/'
end end
def get_latest_version(options, &block)
get_npm_version('jquery-ui', options, &block)
end
end end
end end

@ -21,5 +21,11 @@ module Docs
&copy; 2011&ndash;2017 the contributors to the JSDoc 3 documentation project<br> &copy; 2011&ndash;2017 the contributors to the JSDoc 3 documentation project<br>
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0. Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
HTML HTML
def get_latest_version(options, &block)
get_latest_github_release('jsdoc3', 'jsdoc', options) do |release|
block.call release['tag_name']
end
end
end end
end end

@ -49,5 +49,11 @@ module Docs
html_filters.push 'julia/entries_sphinx', 'julia/clean_html_sphinx', 'sphinx/clean_html' html_filters.push 'julia/entries_sphinx', 'julia/clean_html_sphinx', 'sphinx/clean_html'
end end
def get_latest_version(options, &block)
get_latest_github_release('JuliaLang', 'julia', options) do |release|
block.call release['tag_name'][1..-1]
end
end
end end
end end

@ -33,5 +33,11 @@ module Docs
&copy; Steven Sanderson, the Knockout.js team, and other contributors<br> &copy; Steven Sanderson, the Knockout.js team, and other contributors<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_latest_github_release('knockout', 'knockout', options) do |release|
block.call release['tag_name'][1..-1]
end
end
end end
end end

@ -34,5 +34,9 @@ module Docs
&copy; 2018 Koa contributors<br> &copy; 2018 Koa contributors<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('koa', options, &block)
end
end end
end end

@ -28,5 +28,11 @@ module Docs
&copy; 2010&ndash;2018 JetBrains s.r.o.<br> &copy; 2010&ndash;2018 JetBrains s.r.o.<br>
Licensed under the Apache License, Version 2.0. Licensed under the Apache License, Version 2.0.
HTML HTML
def get_latest_version(options, &block)
get_latest_github_release('JetBrains', 'kotlin', options) do |release|
block.call release['tag_name'][1..-1]
end
end
end end
end end

@ -133,5 +133,11 @@ module Docs
url url
end end
end end
def get_latest_version(options, &block)
get_latest_github_release('laravel', 'laravel', options) do |release|
block.call release['tag_name'][1..-1]
end
end
end end
end end

@ -39,5 +39,11 @@ module Docs
self.base_url = "https://leafletjs.com/reference-#{release}.html" self.base_url = "https://leafletjs.com/reference-#{release}.html"
end end
def get_latest_version(options, &block)
fetch_doc('https://leafletjs.com/index.html', options) do |doc|
link = doc.css('ul > li > a').to_a.select {|node| node.content == 'Docs'}.first
block.call link['href'].scan(/reference-([0-9.]+)\.html/)[0][0]
end
end
end end
end end

@ -21,5 +21,12 @@ module Docs
&copy; 2009&ndash;2016 The Core Less Team<br> &copy; 2009&ndash;2016 The Core Less Team<br>
Licensed under the Creative Commons Attribution License 3.0. Licensed under the Creative Commons Attribution License 3.0.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('http://lesscss.org/features/', options) do |doc|
label = doc.at_css('.footer-links > li').content
block.call label.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -19,5 +19,11 @@ module Docs
&copy; 2005, 2006 Tobias Luetke<br> &copy; 2005, 2006 Tobias Luetke<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_github_tags('Shopify', 'liquid', options) do |tags|
block.call tags[0]['name'][1..-1]
end
end
end end
end end

@ -32,5 +32,11 @@ module Docs
self.release = '2.4.2' self.release = '2.4.2'
self.base_url = "https://lodash.com/docs/#{release}" self.base_url = "https://lodash.com/docs/#{release}"
end end
def get_latest_version(options, &block)
fetch_doc('https://lodash.com/docs/', options) do |doc|
block.call doc.at_css('#version > option[selected]').content
end
end
end end
end end

@ -39,5 +39,11 @@ module Docs
&copy; 2006&ndash;2016 L&Ouml;VE Development Team<br> &copy; 2006&ndash;2016 L&Ouml;VE Development Team<br>
Licensed under the GNU Free Documentation License, Version 1.3. Licensed under the GNU Free Documentation License, Version 1.3.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://love2d.org/wiki/Version_History', options) do |doc|
block.call doc.at_css('#mw-content-text table a').content
end
end
end end
end end

@ -26,5 +26,11 @@ module Docs
self.release = '5.1.5' self.release = '5.1.5'
self.base_url = 'https://www.lua.org/manual/5.1/' self.base_url = 'https://www.lua.org/manual/5.1/'
end end
def get_latest_version(options, &block)
fetch_doc('https://www.lua.org/manual/', options) do |doc|
block.call doc.at_css('p.menubar > a').content
end
end
end end
end end

@ -38,5 +38,9 @@ module Docs
html_filters.push 'marionette/entries_v2' html_filters.push 'marionette/entries_v2'
end end
def get_latest_version(options, &block)
get_npm_version('backbone.marionette', options, &block)
end
end end
end end

@ -64,5 +64,11 @@ module Docs
"https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/"
] ]
end end
def get_latest_version(options, &block)
get_latest_github_release('matplotlib', 'matplotlib', options) do |release|
block.call release['tag_name'][1..-1]
end
end
end end
end end

@ -45,5 +45,11 @@ module Docs
self.base_urls = ['https://guide.meteor.com/v1.3/', "https://docs.meteor.com/v#{self.release}/"] self.base_urls = ['https://guide.meteor.com/v1.3/', "https://docs.meteor.com/v#{self.release}/"]
options[:fix_urls] = nil options[:fix_urls] = nil
end end
def get_latest_version(options, &block)
fetch_doc('https://docs.meteor.com/#/full/', options) do |doc|
block.call doc.at_css('select.version-select > option').content
end
end
end end
end end

@ -18,5 +18,9 @@ module Docs
&copy; 2011&ndash;2018 JS Foundation and contributors<br> &copy; 2011&ndash;2018 JS Foundation and contributors<br>
Licensed under the Creative Commons Attribution 4.0 International License. Licensed under the Creative Commons Attribution 4.0 International License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('mocha', options, &block)
end
end end
end end

@ -15,5 +15,9 @@ module Docs
&copy; 2009&ndash;2017 The Modernizr team<br> &copy; 2009&ndash;2017 The Modernizr team<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_npm_version('modernizr', options, &block)
end
end end
end end

@ -22,5 +22,11 @@ module Docs
&copy; JS Foundation and other contributors<br> &copy; JS Foundation and other contributors<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('http://momentjs.com/', options) do |doc|
block.call doc.at_css('.hero-title > h1 > span').content
end
end
end end
end end

@ -26,5 +26,12 @@ module Docs
&copy; 2010 LearnBoost<br> &copy; 2010 LearnBoost<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
fetch_doc('https://mongoosejs.com/docs/', options) do |doc|
label = doc.at_css('.pure-menu-link').content.strip
block.call label.sub(/Version /, '')
end
end
end end
end end

@ -21,5 +21,11 @@ module Docs
&copy; Ryan Davis, seattle.rb<br> &copy; Ryan Davis, seattle.rb<br>
Licensed under the MIT License. Licensed under the MIT License.
HTML HTML
def get_latest_version(options, &block)
get_github_file_contents('seattlerb', 'minitest', 'History.rdoc', options) do |contents|
block.call contents.scan(/([0-9.]+)/)[0][0]
end
end
end end
end end

@ -93,5 +93,11 @@ module Docs
version '4.1' do version '4.1' do
self.release = '4.1.16' self.release = '4.1.16'
end end
def get_latest_version(options, &block)
get_latest_github_release('rails', 'rails', options) do |release|
block.call release['name']
end
end
end end
end end

@ -84,5 +84,17 @@ module Docs
version '2.2' do version '2.2' do
self.release = '2.2.10' self.release = '2.2.10'
end end
def get_latest_version(options, &block)
get_github_tags('ruby', 'ruby', options) do |tags|
tags.each do |tag|
version = tag['name'].gsub(/_/, '.')[1..-1]
if !/^([0-9.]+)$/.match(version).nil? && version.count('.') == 2
block.call version
break
end
end
end
end
end end
end end

@ -1,4 +1,12 @@
class UpdatesCLI < Thor class UpdatesCLI < Thor
# The GitHub user that is allowed to upload reports
# TODO: Update this before creating a PR
UPLOAD_USER = 'jmerle'
# The repository to create an issue in when uploading the results
# TODO: Update this before creating a PR
UPLOAD_REPO = 'jmerle/devdocs'
def self.to_s def self.to_s
'Updates' 'Updates'
end end
@ -6,10 +14,14 @@ class UpdatesCLI < Thor
def initialize(*args) def initialize(*args)
require 'docs' require 'docs'
require 'progress_bar' require 'progress_bar'
require 'terminal-table'
require 'date'
super super
end end
desc 'check [--verbose] [doc]...', 'Check for outdated documentations' desc 'check [--github-token] [--upload] [--verbose] [doc]...', 'Check for outdated documentations'
option :github_token, :type => :string
option :upload, :type => :boolean
option :verbose, :type => :boolean option :verbose, :type => :boolean
def check(*names) def check(*names)
# Convert names to a list of Scraper instances # Convert names to a list of Scraper instances
@ -19,23 +31,26 @@ class UpdatesCLI < Thor
# Check all documentations for updates when no arguments are given # Check all documentations for updates when no arguments are given
docs = Docs.all if docs.empty? docs = Docs.all if docs.empty?
progress_bar = ::ProgressBar.new docs.length opts = {
progress_bar.write logger: logger
}
results = docs.map do |doc| if options.key?(:github_token)
result = check_doc(doc) opts[:github_token] = options[:github_token]
progress_bar.increment!
result
end end
valid_results = results.select {|result| result.is_a?(Hash)} with_progress_bar do |bar|
bar.max = docs.length
bar.write
end
up_to_date_results = valid_results.select {|result| !result[:is_outdated]} results = docs.map do |doc|
outdated_results = valid_results.select {|result| result[:is_outdated]} result = check_doc(doc, opts)
with_progress_bar(&:increment!)
result
end
log_results('Up-to-date', up_to_date_results) if options[:verbose] and !up_to_date_results.empty? process_results(results)
logger.info("") if options[:verbose] and !up_to_date_results.empty? and !outdated_results.empty?
log_results('Outdated', outdated_results) unless outdated_results.empty?
rescue Docs::DocNotFound => error rescue Docs::DocNotFound => error
logger.error(error) logger.error(error)
logger.info('Run "thor docs:list" to see the list of docs.') logger.info('Run "thor docs:list" to see the list of docs.')
@ -43,53 +58,260 @@ class UpdatesCLI < Thor
private private
def check_doc(doc) def check_doc(doc, options)
# Newer scraper versions always come before older scraper versions # Newer scraper versions always come before older scraper versions
# Therefore, the first item's release value is the latest current scraper version # Therefore, the first item's release value is the latest scraper version
# #
# For example, a scraper could scrape 3 versions: 10, 11 and 12 # For example, a scraper could scrape 3 versions: 10, 11 and 12
# doc.versions.first would be the scraper for version 12 # doc.versions.first would be the scraper for version 12
instance = doc.versions.first.new instance = doc.versions.first.new
return nil unless instance.class.method_defined?(:options) scraper_version = instance.class.method_defined?(:options) ? instance.options[:release] : nil
return error_result(doc, '`options[:release]` does not exist') if scraper_version.nil?
current_version = instance.options[:release]
return nil if current_version.nil?
logger.debug("Checking #{doc.name}") logger.debug("Checking #{doc.name}")
instance.get_latest_version do |latest_version| instance.get_latest_version(options) do |latest_version|
return { return {
name: doc.name, name: doc.name,
current_version: current_version, scraper_version: scraper_version,
latest_version: latest_version, latest_version: latest_version,
is_outdated: instance.is_outdated(current_version, latest_version) is_outdated: instance.is_outdated(scraper_version, latest_version)
} }
end end
return nil
rescue NotImplementedError rescue NotImplementedError
logger.warn("Couldn't check #{doc.name}, get_latest_version is not implemented") logger.warn("Couldn't check #{doc.name}, get_latest_version is not implemented")
error_result(doc, '`get_latest_version` is not implemented')
rescue rescue
logger.error("Error while checking #{doc.name}") logger.error("Error while checking #{doc.name}")
raise raise
end end
def log_results(label, results) def error_result(doc, reason)
logger.info("#{label} documentations (#{results.length}):") {
name: doc.name,
error: reason
}
end
def process_results(results)
successful_results = results.select {|result| result.key?(:is_outdated)}
failed_results = results.select {|result| result.key?(:error)}
up_to_date_results = successful_results.select {|result| !result[:is_outdated]}
outdated_results = successful_results.select {|result| result[:is_outdated]}
log_results(outdated_results, up_to_date_results, failed_results)
upload_results(outdated_results, up_to_date_results, failed_results) if options[:upload]
end
#
# Result logging methods
#
def log_results(outdated_results, up_to_date_results, failed_results)
log_failed_results(failed_results) unless failed_results.empty?
log_successful_results('Up-to-date', up_to_date_results) unless up_to_date_results.empty?
log_successful_results('Outdated', outdated_results) unless outdated_results.empty?
end
def log_successful_results(label, results)
title = "#{label} documentations (#{results.length})"
headings = ['Documentation', 'Scraper version', 'Latest version']
rows = results.map {|result| [result[:name], result[:scraper_version], result[:latest_version]]}
table = Terminal::Table.new :title => title, :headings => headings, :rows => rows
puts table
end
def log_failed_results(results)
title = "Documentations that could not be checked (#{results.length})"
headings = %w(Documentation Reason)
rows = results.map {|result| [result[:name], result[:error]]}
table = Terminal::Table.new :title => title, :headings => headings, :rows => rows
puts table
end
#
# Upload methods
#
def upload_results(outdated_results, up_to_date_results, failed_results)
# We can't create issues without a GitHub token
unless options.key?(:github_token)
logger.error('Please specify a GitHub token with the public_repo permission for devdocs-bot with the --github-token parameter')
return
end
logger.info('Uploading the results to a new GitHub issue')
logger.info('Checking if the GitHub token belongs to the correct user')
github_get('/user') do |user|
# Only allow the DevDocs bot to upload reports
if user['login'] == UPLOAD_USER
issue = results_to_issue(outdated_results, up_to_date_results, failed_results)
logger.info('Creating a new GitHub issue')
github_post("/repos/#{UPLOAD_REPO}/issues", issue) do |created_issue|
search_params = {
q: "Documentation versions report in:title author:#{UPLOAD_USER} is:issue repo:#{UPLOAD_REPO}",
sort: 'created',
order: 'desc'
}
logger.info('Checking if the previous issue is still open')
github_get('/search/issues', search_params) do |matching_issues|
previous_issue = matching_issues['items'].find {|item| item['number'] != created_issue['number']}
if previous_issue.nil?
logger.info('No previous issue found')
log_upload_success(created_issue)
else
comment = "This report was superseded by ##{created_issue['number']}."
logger.info('Commenting on the previous issue')
github_post("/repos/#{UPLOAD_REPO}/issues/#{previous_issue['number']}/comments", {body: comment}) do |_|
if previous_issue['closed_at'].nil?
logger.info('Closing the previous issue')
github_patch("/repos/#{UPLOAD_REPO}/issues/#{previous_issue['number']}", {state: 'closed'}) do |_|
log_upload_success(created_issue)
end
else
logger.info('The previous issue has already been closed')
log_upload_success(created_issue)
end
end
end
end
end
else
logger.error("Only #{UPLOAD_USER} is supposed to upload the results to a new issue. The specified github token is not for #{UPLOAD_USER}.")
end
end
end
def results_to_issue(outdated_results, up_to_date_results, failed_results)
results = [
successful_results_to_markdown('Outdated', outdated_results),
successful_results_to_markdown('Up-to-date', up_to_date_results),
failed_results_to_markdown(failed_results)
]
results_str = results.select {|result| !result.nil?}.join("\n\n")
title = "Documentation versions report for #{Date.today.strftime('%B')} 2019"
body = <<-MARKDOWN
## What is this?
This is an automatically created issue which contains information about the version status of the documentations available on DevDocs. The results of this report can be used by maintainers when updating outdated documentations.
Maintainers can close this issue when all documentations are up-to-date. This issue is automatically closed when the next report is created.
## Results
The #{outdated_results.length + up_to_date_results.length + failed_results.length} documentations are divided as follows:
- #{outdated_results.length} that #{outdated_results.length == 1 ? 'is' : 'are'} outdated
- #{up_to_date_results.length} that #{up_to_date_results.length == 1 ? 'is' : 'are'} up-to-date (patch updates are ignored)
- #{failed_results.length} that could not be checked
MARKDOWN
{
title: title,
body: body.strip + "\n\n" + results_str
}
end
def successful_results_to_markdown(label, results)
return nil if results.empty?
title = "#{label} documentations (#{results.length})"
headings = ['Documentation', 'Scraper version', 'Latest version']
rows = results.map {|result| [result[:name], result[:scraper_version], result[:latest_version]]}
results_to_markdown(title, headings, rows)
end
def failed_results_to_markdown(results)
return nil if results.empty?
title = "Documentations that could not be checked (#{results.length})"
headings = %w(Documentation Reason)
rows = results.map {|result| [result[:name], result[:error]]}
results_to_markdown(title, headings, rows)
end
def results_to_markdown(title, headings, rows)
"<details>\n<summary>#{title}</summary>\n\n#{create_markdown_table(headings, rows)}\n</details>"
end
def create_markdown_table(headings, rows)
header = headings.join(' | ')
separator = '-|' * headings.length
body = rows.map {|row| row.join(' | ')}
header + "\n" + separator[0...-1] + "\n" + body.join("\n")
end
def log_upload_success(created_issue)
logger.info("Successfully uploaded the results to #{created_issue['html_url']}")
end
#
# HTTP utilities
#
def github_get(endpoint, params = {}, &block)
github_request(endpoint, {method: :get, params: params}, &block)
end
def github_post(endpoint, params, &block)
github_request(endpoint, {method: :post, body: params.to_json}, &block)
end
def github_patch(endpoint, params, &block)
github_request(endpoint, {method: :patch, body: params.to_json}, &block)
end
results.each do |result| def github_request(endpoint, opts, &block)
logger.info("#{result[:name]}: #{result[:current_version]} -> #{result[:latest_version]}") url = "https://api.github.com#{endpoint}"
# GitHub token authentication
opts[:headers] = {
Authorization: "token #{options[:github_token]}"
}
# GitHub requires the Content-Type to be application/json when a body is passed
if opts.key?(:body)
opts[:headers]['Content-Type'] = 'application/json'
end end
logger.debug("Making a #{opts[:method]} request to #{url}")
Docs::Request.run(url, opts) do |response|
# response.success? is false if the response code is 201
# GitHub returns 201 Created after an issue is created
if response.success? || response.code == 201
block.call JSON.parse(response.body)
else
logger.error("Couldn't make a #{opts[:method]} request to #{url} (response code #{response.code})")
block.call nil
end
end
end
# A utility method which ensures no progress bar is shown when stdout is not a tty
def with_progress_bar(&block)
return unless $stdout.tty?
@progress_bar ||= ::ProgressBar.new
block.call @progress_bar
end end
def logger def logger
@logger ||= Logger.new($stdout).tap do |logger| @logger ||= Logger.new($stdout).tap do |logger|
logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
logger.formatter = proc do |severity, datetime, progname, msg| logger.formatter = proc {|severity, datetime, progname, msg| "[#{severity}] #{msg}\n"}
prefix = severity != "INFO" ? "[#{severity}] " : ""
"#{prefix}#{msg}\n"
end
end end
end end
end end

Loading…
Cancel
Save