Merge pull request #8137 from waldyrious/add-desc-stanza-to-cask-dsl
Add a `desc` stanza to the cask DSL
This commit is contained in:
commit
d5dfe4d6a1
@ -1,5 +1,9 @@
|
|||||||
inherit_from: ./Homebrew/.rubocop.yml
|
inherit_from: ./Homebrew/.rubocop.yml
|
||||||
|
|
||||||
|
Cask/Desc:
|
||||||
|
Description: 'Ensure that the desc stanza conforms to various content and style checks.'
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
Cask/HomepageMatchesUrl:
|
Cask/HomepageMatchesUrl:
|
||||||
Description: 'Ensure that the homepage and url match, otherwise add a comment. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#when-url-and-homepage-hostnames-differ-add-a-comment'
|
Description: 'Ensure that the homepage and url match, otherwise add a comment. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/stanzas/url.md#when-url-and-homepage-hostnames-differ-add-a-comment'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
@ -13,11 +17,11 @@ Cask/NoDslVersion:
|
|||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Cask/StanzaGrouping:
|
Cask/StanzaGrouping:
|
||||||
Description: 'Ensure that cask stanzas are grouped correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order'
|
Description: 'Ensure that cask stanzas are grouped correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
Cask/StanzaOrder:
|
Cask/StanzaOrder:
|
||||||
Description: 'Ensure that cask stanzas are sorted correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order'
|
Description: 'Ensure that cask stanzas are sorted correctly. More info at https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order'
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
# don't want these for casks but re-enabled for Library/Homebrew
|
# don't want these for casks but re-enabled for Library/Homebrew
|
||||||
|
|||||||
@ -155,6 +155,7 @@ module Cask
|
|||||||
{
|
{
|
||||||
"token" => token,
|
"token" => token,
|
||||||
"name" => name,
|
"name" => name,
|
||||||
|
"desc" => desc,
|
||||||
"homepage" => homepage,
|
"homepage" => homepage,
|
||||||
"url" => url,
|
"url" => url,
|
||||||
"appcast" => appcast,
|
"appcast" => appcast,
|
||||||
|
|||||||
@ -30,6 +30,7 @@ module Cask
|
|||||||
|
|
||||||
url "https://"
|
url "https://"
|
||||||
name ""
|
name ""
|
||||||
|
desc ""
|
||||||
homepage ""
|
homepage ""
|
||||||
|
|
||||||
app ""
|
app ""
|
||||||
|
|||||||
@ -64,6 +64,7 @@ module Cask
|
|||||||
:caveats,
|
:caveats,
|
||||||
:conflicts_with,
|
:conflicts_with,
|
||||||
:container,
|
:container,
|
||||||
|
:desc,
|
||||||
:depends_on,
|
:depends_on,
|
||||||
:homepage,
|
:homepage,
|
||||||
:language,
|
:language,
|
||||||
@ -93,6 +94,10 @@ module Cask
|
|||||||
@name.concat(args.flatten)
|
@name.concat(args.flatten)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def desc(description = nil)
|
||||||
|
set_unique_stanza(:desc, description.nil?) { description }
|
||||||
|
end
|
||||||
|
|
||||||
def set_unique_stanza(stanza, should_return)
|
def set_unique_stanza(stanza, should_return)
|
||||||
return instance_variable_get("@#{stanza}") if should_return
|
return instance_variable_get("@#{stanza}") if should_return
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,13 @@ module RuboCop
|
|||||||
end
|
end
|
||||||
|
|
||||||
def cask_token
|
def cask_token
|
||||||
@cask_token ||= pair_node.val_node.children.first
|
@cask_token ||= begin
|
||||||
|
if dsl_version?
|
||||||
|
pair_node.val_node.children.first
|
||||||
|
else
|
||||||
|
method_node.first_argument.str_content
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hash_node
|
def hash_node
|
||||||
|
|||||||
@ -6,7 +6,7 @@ module RuboCop
|
|||||||
module Constants
|
module Constants
|
||||||
STANZA_GROUPS = [
|
STANZA_GROUPS = [
|
||||||
[:version, :sha256],
|
[:version, :sha256],
|
||||||
[:url, :appcast, :name, :homepage],
|
[:url, :appcast, :desc, :name, :homepage],
|
||||||
[
|
[
|
||||||
:auto_updates,
|
:auto_updates,
|
||||||
:conflicts_with,
|
:conflicts_with,
|
||||||
|
|||||||
30
Library/Homebrew/rubocops/cask/desc.rb
Normal file
30
Library/Homebrew/rubocops/cask/desc.rb
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "forwardable"
|
||||||
|
require "uri"
|
||||||
|
require "rubocops/cask/mixin/on_desc_stanza"
|
||||||
|
require "rubocops/shared/desc_helper"
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Cask
|
||||||
|
# This cop audits `desc` in Casks.
|
||||||
|
# See the `DescHelper` module for details of the checks.
|
||||||
|
class Desc < Cop
|
||||||
|
include OnDescStanza
|
||||||
|
include DescHelper
|
||||||
|
|
||||||
|
def on_desc_stanza(stanza)
|
||||||
|
name = cask_block.header.cask_token
|
||||||
|
desc_call = stanza.stanza_node
|
||||||
|
audit_desc(:cask, name, desc_call)
|
||||||
|
end
|
||||||
|
|
||||||
|
def autocorrect(node)
|
||||||
|
name = cask_block.header.cask_token
|
||||||
|
autocorrect_desc(node, name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
28
Library/Homebrew/rubocops/cask/mixin/on_desc_stanza.rb
Normal file
28
Library/Homebrew/rubocops/cask/mixin/on_desc_stanza.rb
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Cask
|
||||||
|
# Common functionality for checking desc stanzas.
|
||||||
|
module OnDescStanza
|
||||||
|
extend Forwardable
|
||||||
|
include CaskHelp
|
||||||
|
|
||||||
|
def on_cask(cask_block)
|
||||||
|
@cask_block = cask_block
|
||||||
|
|
||||||
|
toplevel_stanzas.select(&:desc?).each do |stanza|
|
||||||
|
on_desc_stanza(stanza)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
attr_reader :cask_block
|
||||||
|
|
||||||
|
def_delegators :cask_block,
|
||||||
|
:toplevel_stanzas
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -6,7 +6,7 @@ module RuboCop
|
|||||||
module Cop
|
module Cop
|
||||||
module Cask
|
module Cask
|
||||||
# This cop checks that a cask's stanzas are grouped correctly.
|
# This cop checks that a cask's stanzas are grouped correctly.
|
||||||
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order
|
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order
|
||||||
# for more info.
|
# for more info.
|
||||||
class StanzaGrouping < Cop
|
class StanzaGrouping < Cop
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
|
|||||||
@ -6,7 +6,7 @@ module RuboCop
|
|||||||
module Cop
|
module Cop
|
||||||
module Cask
|
module Cask
|
||||||
# This cop checks that a cask's stanzas are ordered correctly.
|
# This cop checks that a cask's stanzas are ordered correctly.
|
||||||
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/CONTRIBUTING.md#stanza-order
|
# See https://github.com/Homebrew/homebrew-cask/blob/HEAD/doc/cask_language_reference/readme.md#stanza-order
|
||||||
# for more info.
|
# for more info.
|
||||||
class StanzaOrder < Cop
|
class StanzaOrder < Cop
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
|
|||||||
@ -10,11 +10,13 @@ ensure
|
|||||||
end
|
end
|
||||||
|
|
||||||
require "extend/string"
|
require "extend/string"
|
||||||
|
require "rubocops/shared/helper_functions"
|
||||||
|
|
||||||
module RuboCop
|
module RuboCop
|
||||||
module Cop
|
module Cop
|
||||||
class FormulaCop < Cop
|
class FormulaCop < Cop
|
||||||
include RangeHelp
|
include RangeHelp
|
||||||
|
include HelperFunctions
|
||||||
|
|
||||||
attr_accessor :file_path
|
attr_accessor :file_path
|
||||||
|
|
||||||
@ -32,28 +34,6 @@ module RuboCop
|
|||||||
audit_formula(node, class_node, parent_class_node, @body)
|
audit_formula(node, class_node, parent_class_node, @body)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks for regex match of pattern in the node and
|
|
||||||
# sets the appropriate instance variables to report the match
|
|
||||||
def regex_match_group(node, pattern)
|
|
||||||
string_repr = string_content(node).encode("UTF-8", invalid: :replace)
|
|
||||||
match_object = string_repr.match(pattern)
|
|
||||||
return unless match_object
|
|
||||||
|
|
||||||
node_begin_pos = start_column(node)
|
|
||||||
line_begin_pos = line_start_column(node)
|
|
||||||
@column = if node_begin_pos == line_begin_pos
|
|
||||||
node_begin_pos + match_object.begin(0) - line_begin_pos
|
|
||||||
else
|
|
||||||
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
|
|
||||||
end
|
|
||||||
@length = match_object.to_s.length
|
|
||||||
@line_no = line_number(node)
|
|
||||||
@source_buf = source_buffer(node)
|
|
||||||
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
|
|
||||||
@offensive_node = node
|
|
||||||
match_object
|
|
||||||
end
|
|
||||||
|
|
||||||
# Yields to block when there is a match.
|
# Yields to block when there is a match.
|
||||||
# @param urls [Array] url/mirror method call nodes
|
# @param urls [Array] url/mirror method call nodes
|
||||||
# @param regex [Regexp] pattern to match urls
|
# @param regex [Regexp] pattern to match urls
|
||||||
@ -442,26 +422,11 @@ module RuboCop
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the begin position of the node's line in source code
|
|
||||||
def line_start_column(node)
|
|
||||||
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the begin position of the node in source code
|
|
||||||
def start_column(node)
|
|
||||||
node.source_range.begin_pos
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the ending position of the node in source code
|
# Returns the ending position of the node in source code
|
||||||
def end_column(node)
|
def end_column(node)
|
||||||
node.source_range.end_pos
|
node.source_range.end_pos
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the line number of the node
|
|
||||||
def line_number(node)
|
|
||||||
node.loc.line
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the class node's name, nil if not a class node
|
# Returns the class node's name, nil if not a class node
|
||||||
def class_name(node)
|
def class_name(node)
|
||||||
@offensive_node = node
|
@offensive_node = node
|
||||||
@ -484,35 +449,6 @@ module RuboCop
|
|||||||
block.loc.end.line - block.loc.begin.line
|
block.loc.end.line - block.loc.begin.line
|
||||||
end
|
end
|
||||||
|
|
||||||
# Source buffer is required as an argument to report style violations
|
|
||||||
def source_buffer(node)
|
|
||||||
node.source_range.source_buffer
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns the string representation if node is of type str(plain) or dstr(interpolated) or const
|
|
||||||
def string_content(node)
|
|
||||||
case node.type
|
|
||||||
when :str
|
|
||||||
node.str_content
|
|
||||||
when :dstr
|
|
||||||
content = ""
|
|
||||||
node.each_child_node(:str, :begin) do |child|
|
|
||||||
content += if child.begin_type?
|
|
||||||
child.source
|
|
||||||
else
|
|
||||||
child.str_content
|
|
||||||
end
|
|
||||||
end
|
|
||||||
content
|
|
||||||
when :const
|
|
||||||
node.const_name
|
|
||||||
when :sym
|
|
||||||
node.children.first.to_s
|
|
||||||
else
|
|
||||||
""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns true if the formula is versioned
|
# Returns true if the formula is versioned
|
||||||
def versioned_formula?
|
def versioned_formula?
|
||||||
@formula_name.include?("@")
|
@formula_name.include?("@")
|
||||||
@ -532,10 +468,6 @@ module RuboCop
|
|||||||
match_obj[1]
|
match_obj[1]
|
||||||
end
|
end
|
||||||
|
|
||||||
def problem(msg)
|
|
||||||
add_offense(@offensive_node, location: @offense_source_range, message: msg)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def formula_class?(node)
|
def formula_class?(node)
|
||||||
|
|||||||
@ -1,105 +1,24 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "rubocops/extend/formula"
|
require "rubocops/extend/formula"
|
||||||
|
require "rubocops/shared/desc_helper"
|
||||||
require "extend/string"
|
require "extend/string"
|
||||||
|
|
||||||
module RuboCop
|
module RuboCop
|
||||||
module Cop
|
module Cop
|
||||||
module FormulaAudit
|
module FormulaAudit
|
||||||
# This cop audits `desc` in Formulae.
|
# This cop audits `desc` in Formulae.
|
||||||
#
|
# See the `DescHelper` module for details of the checks.
|
||||||
# - Checks for existence of `desc`
|
|
||||||
# - Checks if size of `desc` > 80
|
|
||||||
# - Checks for leading/trailing whitespace in `desc`
|
|
||||||
# - Checks if `desc` begins with an article
|
|
||||||
# - Checks for correct usage of `command-line` in `desc`
|
|
||||||
# - Checks description starts with a capital letter
|
|
||||||
# - Checks if `desc` contains the formula name
|
|
||||||
# - Checks if `desc` ends with a full stop (apart from in the case of "etc.")
|
|
||||||
class Desc < FormulaCop
|
class Desc < FormulaCop
|
||||||
VALID_LOWERCASE_WORDS = %w[
|
include DescHelper
|
||||||
macOS
|
|
||||||
].freeze
|
|
||||||
|
|
||||||
def audit_formula(_node, _class_node, _parent_class_node, body_node)
|
def audit_formula(_node, _class_node, _parent_class_node, body_node)
|
||||||
desc_call = find_node_method_by_name(body_node, :desc)
|
desc_call = find_node_method_by_name(body_node, :desc)
|
||||||
|
audit_desc(:formula, @formula_name, desc_call)
|
||||||
# Check if a formula's desc is present
|
|
||||||
if desc_call.nil?
|
|
||||||
problem "Formula should have a desc (Description)."
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
desc = parameters(desc_call).first
|
|
||||||
|
|
||||||
# Check the formula's desc length. Should be >0 and <80 characters.
|
|
||||||
pure_desc_length = string_content(desc).length
|
|
||||||
if pure_desc_length.zero?
|
|
||||||
problem "The desc (description) should not be an empty string."
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check for leading whitespace.
|
|
||||||
problem "Description shouldn't have a leading space" if regex_match_group(desc, /^\s+/)
|
|
||||||
|
|
||||||
# Check for trailing whitespace.
|
|
||||||
problem "Description shouldn't have a trailing space" if regex_match_group(desc, /\s+$/)
|
|
||||||
|
|
||||||
# Check if command-line is wrongly used in formula's desc
|
|
||||||
if match = regex_match_group(desc, /(command ?line)/i)
|
|
||||||
c = match.to_s[0]
|
|
||||||
problem "Description should use \"#{c}ommand-line\" instead of \"#{match}\""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if a/an are used in a formula's desc
|
|
||||||
if match = regex_match_group(desc, /^(an?)\s/i)
|
|
||||||
problem "Description shouldn't start with an indefinite article, i.e. \"#{match.to_s.strip}\""
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if invalid uppercase words are at the start of a
|
|
||||||
# formula's desc
|
|
||||||
if !VALID_LOWERCASE_WORDS.include?(string_content(desc).split.first) &&
|
|
||||||
regex_match_group(desc, /^[a-z]/)
|
|
||||||
problem "Description should start with a capital letter"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if formula's desc starts with formula's name
|
|
||||||
if regex_match_group(desc, /^#{@formula_name} /i)
|
|
||||||
problem "Description shouldn't start with the formula name"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if a full stop is used at the end of a formula's desc (apart from in the case of "etc.")
|
|
||||||
if regex_match_group(desc, /\.$/) && !string_content(desc).end_with?("etc.")
|
|
||||||
problem "Description shouldn't end with a full stop"
|
|
||||||
end
|
|
||||||
|
|
||||||
desc_length = "#{@formula_name}: #{string_content(desc)}".length
|
|
||||||
max_desc_length = 80
|
|
||||||
return if desc_length <= max_desc_length
|
|
||||||
|
|
||||||
problem "Description is too long. \"name: desc\" should be less than #{max_desc_length} characters. " \
|
|
||||||
"Length is calculated as #{@formula_name} + desc. (currently #{desc_length})"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def autocorrect(node)
|
def autocorrect(node)
|
||||||
lambda do |corrector|
|
autocorrect_desc(node, @formula_name)
|
||||||
correction = node.source
|
|
||||||
first_word = string_content(node).split.first
|
|
||||||
unless VALID_LOWERCASE_WORDS.include?(first_word)
|
|
||||||
first_char = first_word.to_s[0]
|
|
||||||
correction.sub!(/^(['"]?)([a-z])/, "\\1#{first_char.upcase}") if first_char
|
|
||||||
end
|
|
||||||
correction.sub!(/^(['"]?)an?\s/i, "\\1")
|
|
||||||
correction.gsub!(/(ommand ?line)/i, "ommand-line")
|
|
||||||
correction.gsub!(/(^|[^a-z])#{@formula_name}([^a-z]|$)/i, "\\1\\2")
|
|
||||||
correction.gsub!(/^(['"]?)\s+/, "\\1")
|
|
||||||
correction.gsub!(/\s+(['"]?)$/, "\\1")
|
|
||||||
correction.gsub!(/\.(['"]?)$/, "\\1")
|
|
||||||
correction.gsub!(/^\s+/, "")
|
|
||||||
correction.gsub!(/\s+$/, "")
|
|
||||||
corrector.insert_before(node.source_range, correction)
|
|
||||||
corrector.remove(node.source_range)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -11,6 +11,7 @@ require "rubocops/cask/extend/string"
|
|||||||
require "rubocops/cask/extend/node"
|
require "rubocops/cask/extend/node"
|
||||||
require "rubocops/cask/mixin/cask_help"
|
require "rubocops/cask/mixin/cask_help"
|
||||||
require "rubocops/cask/mixin/on_homepage_stanza"
|
require "rubocops/cask/mixin/on_homepage_stanza"
|
||||||
|
require "rubocops/cask/desc"
|
||||||
require "rubocops/cask/homepage_matches_url"
|
require "rubocops/cask/homepage_matches_url"
|
||||||
require "rubocops/cask/homepage_url_trailing_slash"
|
require "rubocops/cask/homepage_url_trailing_slash"
|
||||||
require "rubocops/cask/no_dsl_version"
|
require "rubocops/cask/no_dsl_version"
|
||||||
|
|||||||
99
Library/Homebrew/rubocops/shared/desc_helper.rb
Normal file
99
Library/Homebrew/rubocops/shared/desc_helper.rb
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rubocops/shared/helper_functions"
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
# This module performs common checks the `desc` field in both Formulae and Casks.
|
||||||
|
module DescHelper
|
||||||
|
include HelperFunctions
|
||||||
|
|
||||||
|
VALID_LOWERCASE_WORDS = %w[
|
||||||
|
macOS
|
||||||
|
].freeze
|
||||||
|
|
||||||
|
def audit_desc(type, name, desc_call)
|
||||||
|
# Check if a desc is present.
|
||||||
|
if desc_call.nil?
|
||||||
|
problem "#{type.to_s.capitalize} should have a desc (Description)."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
desc = desc_call.first_argument
|
||||||
|
|
||||||
|
# Check if the desc is empty.
|
||||||
|
pure_desc_length = string_content(desc).length
|
||||||
|
if pure_desc_length.zero?
|
||||||
|
problem "The desc (description) should not be an empty string."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check the desc for leading whitespace.
|
||||||
|
problem "Description shouldn't have leading spaces." if regex_match_group(desc, /^\s+/)
|
||||||
|
|
||||||
|
# Check the desc for trailing whitespace.
|
||||||
|
problem "Description shouldn't have trailing spaces." if regex_match_group(desc, /\s+$/)
|
||||||
|
|
||||||
|
# Check if "command-line" is spelled incorrectly in the desc.
|
||||||
|
if match = regex_match_group(desc, /(command ?line)/i)
|
||||||
|
c = match.to_s[0]
|
||||||
|
problem "Description should use \"#{c}ommand-line\" instead of \"#{match}\"."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the desc starts with "A" or "An".
|
||||||
|
if match = regex_match_group(desc, /^(an?)(?=\s)/i)
|
||||||
|
problem "Description shouldn't start with an indefinite article, i.e. \"#{match}\"."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if invalid lowercase words are at the start of a desc.
|
||||||
|
if !VALID_LOWERCASE_WORDS.include?(string_content(desc).split.first) &&
|
||||||
|
regex_match_group(desc, /^[a-z]/)
|
||||||
|
problem "Description should start with a capital letter."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the desc starts with the formula's or cask's name.
|
||||||
|
problem "Description shouldn't start with the #{type} name." if regex_match_group(desc, /^#{name} /i)
|
||||||
|
|
||||||
|
# Check if a full stop is used at the end of a desc (apart from in the case of "etc.").
|
||||||
|
if regex_match_group(desc, /\.$/) && !string_content(desc).end_with?("etc.")
|
||||||
|
problem "Description shouldn't end with a full stop."
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the desc length exceeds 80 characters.
|
||||||
|
desc_length = "#{name}: #{string_content(desc)}".length
|
||||||
|
max_desc_length = 80
|
||||||
|
return if desc_length <= max_desc_length
|
||||||
|
|
||||||
|
problem "Description is too long. \"name: desc\" should be less than #{max_desc_length} characters. " \
|
||||||
|
"The current combined length is #{desc_length}."
|
||||||
|
end
|
||||||
|
|
||||||
|
def autocorrect_desc(node, name)
|
||||||
|
lambda do |corrector|
|
||||||
|
/\A(?<quote>["'])(?<correction>.*)(?:\k<quote>)\Z/ =~ node.source
|
||||||
|
|
||||||
|
next if correction.nil?
|
||||||
|
|
||||||
|
correction.gsub!(/^\s+/, "")
|
||||||
|
correction.gsub!(/\s+$/, "")
|
||||||
|
|
||||||
|
correction.sub!(/^an?\s+/i, "")
|
||||||
|
|
||||||
|
first_word = correction.split.first
|
||||||
|
unless VALID_LOWERCASE_WORDS.include?(first_word)
|
||||||
|
first_char = first_word.to_s[0]
|
||||||
|
correction[0] = first_char.upcase if first_char
|
||||||
|
end
|
||||||
|
|
||||||
|
correction.gsub!(/(ommand ?line)/i, "ommand-line")
|
||||||
|
correction.gsub!(/(^|[^a-z])#{name}([^a-z]|$)/i, "\\1\\2")
|
||||||
|
correction.gsub!(/^\s+/, "")
|
||||||
|
correction.gsub!(/\s+$/, "")
|
||||||
|
correction.gsub!(/\.$/, "")
|
||||||
|
|
||||||
|
corrector.replace(node.source_range, "#{quote}#{correction}#{quote}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
79
Library/Homebrew/rubocops/shared/helper_functions.rb
Normal file
79
Library/Homebrew/rubocops/shared/helper_functions.rb
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module HelperFunctions
|
||||||
|
include RangeHelp
|
||||||
|
|
||||||
|
# Checks for regex match of pattern in the node and
|
||||||
|
# sets the appropriate instance variables to report the match
|
||||||
|
def regex_match_group(node, pattern)
|
||||||
|
string_repr = string_content(node).encode("UTF-8", invalid: :replace)
|
||||||
|
match_object = string_repr.match(pattern)
|
||||||
|
return unless match_object
|
||||||
|
|
||||||
|
node_begin_pos = start_column(node)
|
||||||
|
line_begin_pos = line_start_column(node)
|
||||||
|
@column = if node_begin_pos == line_begin_pos
|
||||||
|
node_begin_pos + match_object.begin(0) - line_begin_pos
|
||||||
|
else
|
||||||
|
node_begin_pos + match_object.begin(0) - line_begin_pos + 1
|
||||||
|
end
|
||||||
|
@length = match_object.to_s.length
|
||||||
|
@line_no = line_number(node)
|
||||||
|
@source_buf = source_buffer(node)
|
||||||
|
@offense_source_range = source_range(@source_buf, @line_no, @column, @length)
|
||||||
|
@offensive_node = node
|
||||||
|
match_object
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the begin position of the node's line in source code
|
||||||
|
def line_start_column(node)
|
||||||
|
node.source_range.source_buffer.line_range(node.loc.line).begin_pos
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the begin position of the node in source code
|
||||||
|
def start_column(node)
|
||||||
|
node.source_range.begin_pos
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the line number of the node
|
||||||
|
def line_number(node)
|
||||||
|
node.loc.line
|
||||||
|
end
|
||||||
|
|
||||||
|
# Source buffer is required as an argument to report style violations
|
||||||
|
def source_buffer(node)
|
||||||
|
node.source_range.source_buffer
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the string representation if node is of type str(plain) or dstr(interpolated) or const
|
||||||
|
def string_content(node)
|
||||||
|
case node.type
|
||||||
|
when :str
|
||||||
|
node.str_content
|
||||||
|
when :dstr
|
||||||
|
content = ""
|
||||||
|
node.each_child_node(:str, :begin) do |child|
|
||||||
|
content += if child.begin_type?
|
||||||
|
child.source
|
||||||
|
else
|
||||||
|
child.str_content
|
||||||
|
end
|
||||||
|
end
|
||||||
|
content
|
||||||
|
when :const
|
||||||
|
node.const_name
|
||||||
|
when :sym
|
||||||
|
node.children.first.to_s
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def problem(msg)
|
||||||
|
add_offense(@offensive_node, location: @offense_source_range, message: msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -35,6 +35,7 @@ describe Cask::Cmd::Create, :cask do
|
|||||||
|
|
||||||
url "https://"
|
url "https://"
|
||||||
name ""
|
name ""
|
||||||
|
desc ""
|
||||||
homepage ""
|
homepage ""
|
||||||
|
|
||||||
app ""
|
app ""
|
||||||
|
|||||||
@ -90,7 +90,7 @@ describe Cask::Cmd::List, :cask do
|
|||||||
let(:casks) { ["local-caffeine", "local-transmission"] }
|
let(:casks) { ["local-caffeine", "local-transmission"] }
|
||||||
let(:expected_output) {
|
let(:expected_output) {
|
||||||
<<~EOS
|
<<~EOS
|
||||||
[{"token":"local-caffeine","name":[],"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/caffeine.zip","appcast":null,"version":"1.2.3","sha256":"67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94","artifacts":[["Caffeine.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null},{"token":"local-transmission","name":["Transmission"],"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/transmission-2.61.dmg","appcast":null,"version":"2.61","sha256":"e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68","artifacts":[["Transmission.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null}]
|
[{"token":"local-caffeine","name":[],"desc":null,"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/caffeine.zip","appcast":null,"version":"1.2.3","sha256":"67cdb8a02803ef37fdbf7e0be205863172e41a561ca446cd84f0d7ab35a99d94","artifacts":[["Caffeine.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null},{"token":"local-transmission","name":["Transmission"],"desc":null,"homepage":"https://brew.sh/","url":"file:///usr/local/Homebrew/Library/Homebrew/test/support/fixtures/cask/transmission-2.61.dmg","appcast":null,"version":"2.61","sha256":"e44ffa103fbf83f55c8d0b1bea309a43b2880798dae8620b1ee8da5e1095ec68","artifacts":[["Transmission.app"]],"caveats":null,"depends_on":{},"conflicts_with":null,"container":null,"auto_updates":null}]
|
||||||
EOS
|
EOS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -107,6 +107,16 @@ describe Cask::DSL, :cask do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "desc stanza" do
|
||||||
|
it "lets you set the description via a desc stanza" do
|
||||||
|
cask = Cask::Cask.new("desc-cask") do
|
||||||
|
desc "The package's description"
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(cask.desc).to eq("The package's description")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "sha256 stanza" do
|
describe "sha256 stanza" do
|
||||||
it "lets you set checksum via sha256" do
|
it "lets you set checksum via sha256" do
|
||||||
cask = Cask::Cask.new("checksum-cask") do
|
cask = Cask::Cask.new("checksum-cask") do
|
||||||
|
|||||||
52
Library/Homebrew/test/rubocops/cask/desc_spec.rb
Normal file
52
Library/Homebrew/test/rubocops/cask/desc_spec.rb
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "rubocops/rubocop-cask"
|
||||||
|
require "test/rubocops/cask/shared_examples/cask_cop"
|
||||||
|
|
||||||
|
describe RuboCop::Cop::Cask::Desc do
|
||||||
|
include CaskCop
|
||||||
|
|
||||||
|
subject(:cop) { described_class.new }
|
||||||
|
|
||||||
|
context "with incorrect `desc` stanza" do
|
||||||
|
let(:source) {
|
||||||
|
<<~RUBY
|
||||||
|
cask "foo" do
|
||||||
|
desc "A bar program"
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
}
|
||||||
|
let(:correct_source) {
|
||||||
|
<<~RUBY
|
||||||
|
cask "foo" do
|
||||||
|
desc "Bar program"
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
}
|
||||||
|
let(:expected_offenses) do
|
||||||
|
[{
|
||||||
|
message: "Description shouldn't start with an indefinite article, i.e. \"A\".",
|
||||||
|
severity: :convention,
|
||||||
|
line: 2,
|
||||||
|
column: 8,
|
||||||
|
source: "A",
|
||||||
|
}]
|
||||||
|
end
|
||||||
|
|
||||||
|
include_examples "reports offenses"
|
||||||
|
|
||||||
|
include_examples "autocorrects source"
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with correct `desc` stanza" do
|
||||||
|
let(:source) {
|
||||||
|
<<~RUBY
|
||||||
|
cask "foo" do
|
||||||
|
desc "Bar program"
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
}
|
||||||
|
|
||||||
|
include_examples "does not report any offenses"
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -8,9 +8,6 @@ describe RuboCop::Cop::Cask::NoDslVersion do
|
|||||||
|
|
||||||
subject(:cop) { described_class.new }
|
subject(:cop) { described_class.new }
|
||||||
|
|
||||||
context "with header method `cask`" do
|
|
||||||
let(:header_method) { "cask" }
|
|
||||||
|
|
||||||
context "with no dsl version" do
|
context "with no dsl version" do
|
||||||
let(:source) { "cask 'foo' do; end" }
|
let(:source) { "cask 'foo' do; end" }
|
||||||
|
|
||||||
@ -35,4 +32,3 @@ describe RuboCop::Cop::Cask::NoDslVersion do
|
|||||||
include_examples "autocorrects source"
|
include_examples "autocorrects source"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ describe RuboCop::Cop::FormulaAudit::Desc do
|
|||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
it "reports an offense when desc is an empty string" do
|
it "When desc is an empty string" do
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
class Foo < Formula
|
class Foo < Formula
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
@ -30,81 +30,30 @@ describe RuboCop::Cop::FormulaAudit::Desc do
|
|||||||
class Foo < Formula
|
class Foo < Formula
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
desc 'Bar#{"bar" * 29}'
|
desc 'Bar#{"bar" * 29}'
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. Length is calculated as foo + desc. (currently 95)
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. The current combined length is 95.
|
||||||
end
|
end
|
||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
it "When desc is multiline string" do
|
it "When desc is a multiline string" do
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
class Foo < Formula
|
class Foo < Formula
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
desc 'Bar#{"bar" * 9}'\
|
desc 'Bar#{"bar" * 9}'\
|
||||||
'#{"foo" * 21}'
|
'#{"foo" * 21}'
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. Length is calculated as foo + desc. (currently 98)
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Description is too long. "name: desc" should be less than 80 characters. The current combined length is 98.
|
||||||
end
|
end
|
||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context "When auditing formula desc" do
|
context "When auditing formula desc" do
|
||||||
it "When wrong \"command-line\" usage in desc" do
|
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
|
||||||
class Foo < Formula
|
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
|
||||||
desc 'command line'
|
|
||||||
^ Description should start with a capital letter
|
|
||||||
^^^^^^^^^^^^ Description should use \"command-line\" instead of \"command line\"
|
|
||||||
end
|
|
||||||
RUBY
|
|
||||||
end
|
|
||||||
|
|
||||||
it "When an article is used in desc" do
|
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
|
||||||
class Foo < Formula
|
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
|
||||||
desc 'An aardvark'
|
|
||||||
^^^ Description shouldn\'t start with an indefinite article, i.e. \"An\"
|
|
||||||
end
|
|
||||||
RUBY
|
|
||||||
end
|
|
||||||
|
|
||||||
it "When an lowercase letter starts a desc" do
|
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
|
||||||
class Foo < Formula
|
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
|
||||||
desc 'bar'
|
|
||||||
^ Description should start with a capital letter
|
|
||||||
end
|
|
||||||
RUBY
|
|
||||||
end
|
|
||||||
|
|
||||||
it "When formula name is in desc" do
|
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
|
||||||
class Foo < Formula
|
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
|
||||||
desc 'Foo is a foobar'
|
|
||||||
^^^^ Description shouldn\'t start with the formula name
|
|
||||||
end
|
|
||||||
RUBY
|
|
||||||
end
|
|
||||||
|
|
||||||
it "When the description ends with a full stop" do
|
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
|
||||||
class Foo < Formula
|
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
|
||||||
desc 'Description with a full stop at the end.'
|
|
||||||
^ Description shouldn\'t end with a full stop
|
|
||||||
end
|
|
||||||
RUBY
|
|
||||||
end
|
|
||||||
|
|
||||||
it "When the description starts with a leading space" do
|
it "When the description starts with a leading space" do
|
||||||
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
class Foo < Formula
|
class Foo < Formula
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
desc ' Description with a leading space'
|
desc ' Description with a leading space'
|
||||||
^ Description shouldn\'t have a leading space
|
^ Description shouldn\'t have leading spaces.
|
||||||
end
|
end
|
||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
@ -114,7 +63,58 @@ describe RuboCop::Cop::FormulaAudit::Desc do
|
|||||||
class Foo < Formula
|
class Foo < Formula
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
desc 'Description with a trailing space '
|
desc 'Description with a trailing space '
|
||||||
^ Description shouldn\'t have a trailing space
|
^ Description shouldn\'t have trailing spaces.
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it "When \"command-line\" is incorrectly spelled in the desc" do
|
||||||
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
|
class Foo < Formula
|
||||||
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
|
desc 'command line'
|
||||||
|
^ Description should start with a capital letter.
|
||||||
|
^^^^^^^^^^^^ Description should use \"command-line\" instead of \"command line\".
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it "When an article is used in the desc" do
|
||||||
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
|
class Foo < Formula
|
||||||
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
|
desc 'An aardvark'
|
||||||
|
^^ Description shouldn\'t start with an indefinite article, i.e. \"An\".
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it "When the desc starts with a lowercase letter" do
|
||||||
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
|
class Foo < Formula
|
||||||
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
|
desc 'bar'
|
||||||
|
^ Description should start with a capital letter.
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it "When the desc starts with the formula name" do
|
||||||
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
|
class Foo < Formula
|
||||||
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
|
desc 'Foo is a foobar'
|
||||||
|
^^^^ Description shouldn\'t start with the formula name.
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
|
||||||
|
it "When the description ends with a full stop" do
|
||||||
|
expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb")
|
||||||
|
class Foo < Formula
|
||||||
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
|
desc 'Description with a full stop at the end.'
|
||||||
|
^ Description shouldn\'t end with a full stop.
|
||||||
end
|
end
|
||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
@ -130,7 +130,7 @@ describe RuboCop::Cop::FormulaAudit::Desc do
|
|||||||
correct_source = <<~RUBY
|
correct_source = <<~RUBY
|
||||||
class Foo < Formula
|
class Foo < Formula
|
||||||
url 'https://brew.sh/foo-1.0.tgz'
|
url 'https://brew.sh/foo-1.0.tgz'
|
||||||
desc 'an bar: command-line'
|
desc 'Bar: command-line'
|
||||||
end
|
end
|
||||||
RUBY
|
RUBY
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user