2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-10-26 19:49:21 +01:00
|
|
|
require "forwardable"
|
|
|
|
|
|
|
|
module RuboCop
|
|
|
|
module Cop
|
|
|
|
module Cask
|
|
|
|
# This cop checks that a cask's homepage matches the download url,
|
|
|
|
# or if it doesn't, checks if a comment in the form
|
|
|
|
# `# example.com was verified as official when first introduced to the cask`
|
|
|
|
# is present.
|
2020-07-27 12:57:29 +01:00
|
|
|
class HomepageMatchesUrl < Cop
|
2018-10-26 19:49:21 +01:00
|
|
|
extend Forwardable
|
|
|
|
include CaskHelp
|
|
|
|
|
|
|
|
REFERENCE_URL =
|
2020-06-25 11:20:57 +01:00
|
|
|
"https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/" \
|
2019-04-19 15:38:03 +09:00
|
|
|
"cask_language_reference/stanzas/url.md#when-url-and-homepage-hostnames-differ-add-a-comment"
|
2018-10-26 19:49:21 +01:00
|
|
|
|
|
|
|
COMMENT_FORMAT = /# [^ ]+ was verified as official when first introduced to the cask/.freeze
|
|
|
|
|
2019-10-03 08:50:45 +02:00
|
|
|
MSG_NO_MATCH = "`%<url>s` does not match `%<full_url>s`"
|
2018-10-26 19:49:21 +01:00
|
|
|
|
2019-10-03 08:50:45 +02:00
|
|
|
MSG_MISSING = ("`%<domain>s` does not match `%<homepage>s`, a comment has to be added " \
|
2019-04-19 21:46:20 +09:00
|
|
|
"above the `url` stanza. For details, see " + REFERENCE_URL).freeze
|
2018-10-26 19:49:21 +01:00
|
|
|
|
2019-10-03 08:50:45 +02:00
|
|
|
MSG_WRONG_FORMAT = ("`%<comment>s` does not match the expected comment format. " \
|
2019-04-19 21:46:20 +09:00
|
|
|
"For details, see " + REFERENCE_URL).freeze
|
2018-10-26 19:49:21 +01:00
|
|
|
|
2019-10-03 08:50:45 +02:00
|
|
|
MSG_UNNECESSARY = "The URL's domain `%<domain>s` matches the homepage `%<homepage>s`, " \
|
2019-04-19 15:38:03 +09:00
|
|
|
"the comment above the `url` stanza is unnecessary"
|
2018-10-26 19:49:21 +01:00
|
|
|
|
|
|
|
def on_cask(cask_block)
|
|
|
|
@cask_block = cask_block
|
|
|
|
return unless homepage_stanza
|
|
|
|
|
|
|
|
add_offenses
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
attr_reader :cask_block
|
2020-05-12 08:32:27 +01:00
|
|
|
|
2018-10-26 19:49:21 +01:00
|
|
|
def_delegators :cask_block, :cask_node, :toplevel_stanzas,
|
|
|
|
:sorted_toplevel_stanzas
|
|
|
|
|
|
|
|
def add_offenses
|
|
|
|
toplevel_stanzas.select(&:url?).each do |url|
|
|
|
|
next if add_offense_unnecessary_comment(url)
|
|
|
|
next if add_offense_missing_comment(url)
|
|
|
|
next if add_offense_no_match(url)
|
|
|
|
next if add_offense_wrong_format(url)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_offense_unnecessary_comment(stanza)
|
|
|
|
return unless comment?(stanza)
|
|
|
|
return unless url_match_homepage?(stanza)
|
|
|
|
return unless comment_matches_format?(stanza)
|
|
|
|
return unless comment_matches_url?(stanza)
|
|
|
|
|
|
|
|
comment = comment(stanza).loc.expression
|
|
|
|
add_offense(comment,
|
|
|
|
location: comment,
|
|
|
|
message: format(MSG_UNNECESSARY, domain: domain(stanza), homepage: homepage))
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_offense_missing_comment(stanza)
|
|
|
|
return if url_match_homepage?(stanza)
|
|
|
|
return if !url_match_homepage?(stanza) && comment?(stanza)
|
|
|
|
|
|
|
|
range = stanza.source_range
|
|
|
|
url_domain = domain(stanza)
|
|
|
|
add_offense(range, location: range, message: format(MSG_MISSING, domain: url_domain, homepage: homepage))
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_offense_no_match(stanza)
|
|
|
|
return if url_match_homepage?(stanza)
|
|
|
|
return unless comment?(stanza)
|
|
|
|
return if !url_match_homepage?(stanza) && comment_matches_url?(stanza)
|
|
|
|
|
|
|
|
comment = comment(stanza).loc.expression
|
|
|
|
add_offense(comment,
|
|
|
|
location: comment,
|
|
|
|
message: format(MSG_NO_MATCH, url: url_from_comment(stanza), full_url: full_url(stanza)))
|
|
|
|
end
|
|
|
|
|
|
|
|
def add_offense_wrong_format(stanza)
|
|
|
|
return if url_match_homepage?(stanza)
|
|
|
|
return unless comment?(stanza)
|
|
|
|
return if comment_matches_format?(stanza)
|
|
|
|
|
|
|
|
comment = comment(stanza).loc.expression
|
|
|
|
add_offense(comment,
|
|
|
|
location: comment,
|
|
|
|
message: format(MSG_WRONG_FORMAT, comment: comment(stanza).text))
|
|
|
|
end
|
|
|
|
|
|
|
|
def comment?(stanza)
|
|
|
|
!stanza.comments.empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
def comment(stanza)
|
|
|
|
stanza.comments.last
|
|
|
|
end
|
|
|
|
|
|
|
|
def comment_matches_format?(stanza)
|
|
|
|
comment(stanza).text =~ COMMENT_FORMAT
|
|
|
|
end
|
|
|
|
|
|
|
|
def url_from_comment(stanza)
|
|
|
|
comment(stanza).text
|
|
|
|
.sub(/[^ ]*# ([^ ]+) .*/, '\1')
|
|
|
|
end
|
|
|
|
|
|
|
|
def comment_matches_url?(stanza)
|
|
|
|
full_url(stanza).include?(url_from_comment(stanza))
|
|
|
|
end
|
|
|
|
|
|
|
|
def strip_url_scheme(url)
|
|
|
|
url.sub(%r{^.*://(www\.)?}, "")
|
|
|
|
end
|
|
|
|
|
|
|
|
def domain(stanza)
|
|
|
|
strip_url_scheme(extract_url(stanza)).gsub(%r{^([^/]+).*}, '\1')
|
|
|
|
end
|
|
|
|
|
|
|
|
def extract_url(stanza)
|
|
|
|
string = stanza.stanza_node.children[2]
|
|
|
|
return string.str_content if string.str_type?
|
|
|
|
|
2020-06-02 09:49:23 +01:00
|
|
|
string.to_s.gsub(%r{.*"([a-z0-9]+://[^"]+)".*}m, '\1')
|
2018-10-26 19:49:21 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
def url_match_homepage?(stanza)
|
|
|
|
host = extract_url(stanza).downcase
|
2020-07-27 12:57:29 +01:00
|
|
|
host_uri = begin
|
|
|
|
URI(remove_non_ascii(host))
|
|
|
|
rescue URI::InvalidURIError
|
|
|
|
# Can't check if we can't parse.
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2018-10-26 19:49:21 +01:00
|
|
|
host = if host.match?(/:\d/) && host_uri.port != 80
|
|
|
|
"#{host_uri.host}:#{host_uri.port}"
|
|
|
|
else
|
|
|
|
host_uri.host
|
|
|
|
end
|
|
|
|
home = homepage.downcase
|
|
|
|
if (split_host = host.split(".")).length >= 3
|
2020-05-12 08:32:27 +01:00
|
|
|
host = split_host[-2..].join(".")
|
2018-10-26 19:49:21 +01:00
|
|
|
end
|
|
|
|
if (split_home = homepage.split(".")).length >= 3
|
2020-05-12 08:32:27 +01:00
|
|
|
home = split_home[-2..].join(".")
|
2018-10-26 19:49:21 +01:00
|
|
|
end
|
|
|
|
host == home
|
|
|
|
end
|
|
|
|
|
|
|
|
def full_url(stanza)
|
|
|
|
strip_url_scheme(extract_url(stanza))
|
|
|
|
end
|
|
|
|
|
|
|
|
def homepage
|
|
|
|
URI(remove_non_ascii(extract_url(homepage_stanza))).host
|
|
|
|
end
|
|
|
|
|
|
|
|
def homepage_stanza
|
|
|
|
toplevel_stanzas.find(&:homepage?)
|
|
|
|
end
|
|
|
|
|
|
|
|
def remove_non_ascii(string)
|
|
|
|
string.gsub(/\P{ASCII}/, "")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|