fastlane 2.40.0.beta.20170622010014 → 2.40.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/cert/README.md +3 -1
- data/deliver/README.md +5 -1
- data/deliver/lib/deliver/options.rb +16 -0
- data/deliver/lib/deliver/runner.rb +36 -2
- data/fastlane/README.md +3 -1
- data/fastlane/lib/fastlane/actions/precheck.rb +52 -0
- data/fastlane/lib/fastlane/tools.rb +2 -1
- data/fastlane/lib/fastlane/version.rb +1 -1
- data/fastlane_core/README.md +2 -1
- data/frameit/README.md +3 -1
- data/gym/README.md +3 -1
- data/match/README.md +3 -1
- data/pem/README.md +3 -1
- data/pilot/README.md +3 -1
- data/precheck/README.md +174 -0
- data/precheck/lib/assets/PrecheckfileTemplate +28 -0
- data/precheck/lib/precheck.rb +22 -0
- data/precheck/lib/precheck/commands_generator.rb +56 -0
- data/precheck/lib/precheck/item_to_check.rb +58 -0
- data/precheck/lib/precheck/options.rb +63 -0
- data/precheck/lib/precheck/rule.rb +169 -0
- data/precheck/lib/precheck/rule_check_result.rb +19 -0
- data/precheck/lib/precheck/rule_processor.rb +199 -0
- data/precheck/lib/precheck/rules/abstract_text_match_rule.rb +64 -0
- data/precheck/lib/precheck/rules/copyright_date_rule.rb +38 -0
- data/precheck/lib/precheck/rules/curse_words_rule.rb +57 -0
- data/precheck/lib/precheck/rules/custom_text_rule.rb +36 -0
- data/precheck/lib/precheck/rules/future_functionality_rule.rb +34 -0
- data/precheck/lib/precheck/rules/negative_apple_sentiment_rule.rb +38 -0
- data/precheck/lib/precheck/rules/other_platforms_rule.rb +38 -0
- data/precheck/lib/precheck/rules/placeholder_words_rule.rb +33 -0
- data/precheck/lib/precheck/rules/rules_data/curse_word_hashes/en_us.txt +349 -0
- data/precheck/lib/precheck/rules/test_words_rule.rb +31 -0
- data/precheck/lib/precheck/rules/unreachable_urls_rule.rb +42 -0
- data/precheck/lib/precheck/runner.rb +164 -0
- data/produce/README.md +3 -1
- data/scan/README.md +3 -1
- data/screengrab/README.md +2 -1
- data/sigh/README.md +3 -1
- data/snapshot/README.md +3 -1
- data/spaceship/README.md +2 -1
- data/spaceship/lib/spaceship/tunes/language_item.rb +5 -1
- data/supply/README.md +3 -1
- metadata +38 -14
@@ -0,0 +1,28 @@
|
|
1
|
+
# For more information about this configuration visit
|
2
|
+
# https://github.com/fastlane/fastlane/tree/master/precheck#precheckfile
|
3
|
+
|
4
|
+
# In general, you can use the options available
|
5
|
+
# fastlane precheck --help
|
6
|
+
|
7
|
+
# Remove the # in front of the line to enable the option
|
8
|
+
|
9
|
+
# You have three possible values for each rule options
|
10
|
+
# :skip
|
11
|
+
# indicates that your metadata will not be checked by this rule
|
12
|
+
|
13
|
+
# :warn
|
14
|
+
# when triggered, this rule will warn you of a potential problem
|
15
|
+
|
16
|
+
# :fail
|
17
|
+
# when triggered, this rule will cause an error to be displayed and it will prevent any further fastlane commands from running after precheck finishes
|
18
|
+
|
19
|
+
# Examples:
|
20
|
+
# negative_apple_sentiment(level: :skip)
|
21
|
+
# curse_words(level: :warn)
|
22
|
+
# future_functionality(level: :error)
|
23
|
+
# other_platforms(level: :error)
|
24
|
+
# placeholder_text(level: :error)
|
25
|
+
# test_words(level: :error)
|
26
|
+
# unreachable_urls(level: :error)
|
27
|
+
# custom_text(data: ["fabric"], level: :warn)
|
28
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'fastlane_core'
|
2
|
+
require 'precheck/runner'
|
3
|
+
require 'precheck/options'
|
4
|
+
|
5
|
+
module Precheck
|
6
|
+
# Use this to just setup the configuration attribute and set it later somewhere else
|
7
|
+
class << self
|
8
|
+
attr_accessor :config
|
9
|
+
|
10
|
+
def precheckfile_name
|
11
|
+
"Precheckfile"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
|
16
|
+
UI = FastlaneCore::UI
|
17
|
+
ROOT = Pathname.new(File.expand_path('../..', __FILE__))
|
18
|
+
|
19
|
+
ENV['APP_IDENTIFIER'] ||= ENV["PRECHECK_APP_IDENTIFIER"]
|
20
|
+
|
21
|
+
DESCRIPTION = 'Check your app using a community driven set of App Store review rules to avoid being rejected'
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "commander"
|
2
|
+
require "fastlane_core"
|
3
|
+
require "fastlane/version"
|
4
|
+
|
5
|
+
HighLine.track_eof = false
|
6
|
+
|
7
|
+
module Precheck
|
8
|
+
class CommandsGenerator
|
9
|
+
include Commander::Methods
|
10
|
+
|
11
|
+
def self.start
|
12
|
+
new.run
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
program :name, 'precheck'
|
17
|
+
program :version, Fastlane::VERSION
|
18
|
+
program :description, Precheck::DESCRIPTION
|
19
|
+
program :help, "Author", "Joshua Liebowitz <taquitos@gmail.com>, @taquitos"
|
20
|
+
program :help, "Website", "https://fastlane.tools"
|
21
|
+
program :help, "GitHub", "https://github.com/fastlane/fastlane/tree/master/precheck"
|
22
|
+
program :help_formatter, :compact
|
23
|
+
|
24
|
+
global_option("--verbose") { FastlaneCore::Globals.verbose = true }
|
25
|
+
|
26
|
+
command :check_metadata do |c|
|
27
|
+
c.syntax = "fastlane precheck"
|
28
|
+
c.description = Precheck::DESCRIPTION
|
29
|
+
|
30
|
+
FastlaneCore::CommanderGenerator.new.generate(Precheck::Options.available_options, command: c)
|
31
|
+
|
32
|
+
c.action do |_args, options|
|
33
|
+
Precheck.config = FastlaneCore::Configuration.create(Precheck::Options.available_options, options.__hash__)
|
34
|
+
Precheck::Runner.new.run
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
command :init do |c|
|
39
|
+
c.syntax = "fastlane precheck init"
|
40
|
+
c.description = "Creates a new Precheckfile for you"
|
41
|
+
c.action do |_args, options|
|
42
|
+
containing = FastlaneCore::Helper.fastlane_enabled_folder_path
|
43
|
+
path = File.join(containing, Precheck.precheckfile_name)
|
44
|
+
UI.user_error! "Precheckfile already exists" if File.exist?(path)
|
45
|
+
template = File.read("#{Precheck::ROOT}/lib/assets/PrecheckfileTemplate")
|
46
|
+
File.write(path, template)
|
47
|
+
UI.success "Successfully created '#{path}'. Open the file using a code editor."
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
default_command :check_metadata
|
52
|
+
|
53
|
+
run!
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Precheck
|
2
|
+
# each attribute on a app version is a single item.
|
3
|
+
# for example: .name, .keywords, .description, will all have a single item to represent them
|
4
|
+
# which includes their name and a more user-friendly name we can use to print out information
|
5
|
+
class ItemToCheck
|
6
|
+
attr_accessor :item_name
|
7
|
+
attr_accessor :friendly_name
|
8
|
+
attr_accessor :is_optional
|
9
|
+
|
10
|
+
def initialize(item_name, friendly_name, is_optional = false)
|
11
|
+
@item_name = item_name
|
12
|
+
@friendly_name = friendly_name
|
13
|
+
@is_optional = is_optional
|
14
|
+
end
|
15
|
+
|
16
|
+
def item_data
|
17
|
+
not_implemented(__method__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def inspect
|
21
|
+
"#{self.class}(friendly_name: #{@friendly_name}, data: #{@item_data})"
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"#{self.class}: #{item_name}: #{friendly_name}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# if the data point we want to check is a text field (like 'description'), we'll use this object to encapsulate it
|
30
|
+
# this includes the text, the property name, and what that name maps to in plain english so that we can print out nice, friendly messages.
|
31
|
+
class TextItemToCheck < ItemToCheck
|
32
|
+
attr_accessor :text
|
33
|
+
|
34
|
+
def initialize(text, item_name, friendly_name, is_optional = false)
|
35
|
+
@text = text
|
36
|
+
super(item_name, friendly_name, is_optional)
|
37
|
+
end
|
38
|
+
|
39
|
+
def item_data
|
40
|
+
return text
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# if the data point we want to check is a URL field (like 'marketing_url'), we'll use this object to encapsulate it
|
45
|
+
# this includes the url, the property name, and what that name maps to in plain english so that we can print out nice, friendly messages.
|
46
|
+
class URLItemToCheck < ItemToCheck
|
47
|
+
attr_accessor :url
|
48
|
+
|
49
|
+
def initialize(url, item_name, friendly_name, is_optional = false)
|
50
|
+
@url = url
|
51
|
+
super(item_name, friendly_name, is_optional)
|
52
|
+
end
|
53
|
+
|
54
|
+
def item_data
|
55
|
+
return url
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'fastlane_core'
|
2
|
+
require 'credentials_manager'
|
3
|
+
Dir[File.dirname(__FILE__) + '/rules/*.rb'].each { |file| require file }
|
4
|
+
|
5
|
+
module Precheck
|
6
|
+
class Options
|
7
|
+
def self.rules
|
8
|
+
[
|
9
|
+
NegativeAppleSentimentRule,
|
10
|
+
PlaceholderWordsRule,
|
11
|
+
OtherPlatformsRule,
|
12
|
+
FutureFunctionalityRule,
|
13
|
+
TestWordsRule,
|
14
|
+
CurseWordsRule,
|
15
|
+
CustomTextRule,
|
16
|
+
CopyrightDateRule,
|
17
|
+
UnreachableURLRule
|
18
|
+
].map(&:new)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.available_options
|
22
|
+
user = CredentialsManager::AppfileConfig.try_fetch_value(:itunes_connect_id)
|
23
|
+
user ||= CredentialsManager::AppfileConfig.try_fetch_value(:apple_id)
|
24
|
+
|
25
|
+
[
|
26
|
+
FastlaneCore::ConfigItem.new(key: :app_identifier,
|
27
|
+
short_option: "-a",
|
28
|
+
env_name: "PRECHECK_APP_IDENTIFIER",
|
29
|
+
description: "The bundle identifier of your app",
|
30
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier)),
|
31
|
+
FastlaneCore::ConfigItem.new(key: :username,
|
32
|
+
short_option: "-u",
|
33
|
+
env_name: "PRECHECK_USERNAME",
|
34
|
+
description: "Your Apple ID Username",
|
35
|
+
default_value: user),
|
36
|
+
FastlaneCore::ConfigItem.new(key: :team_id,
|
37
|
+
short_option: "-b",
|
38
|
+
env_name: "PRECHECK_TEAM_ID",
|
39
|
+
description: "The ID of your iTunes Connect team if you're in multiple teams",
|
40
|
+
optional: true,
|
41
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:team_id),
|
42
|
+
verify_block: proc do |value|
|
43
|
+
ENV["FASTLANE_ITC_TEAM_ID"] = value.to_s
|
44
|
+
end),
|
45
|
+
FastlaneCore::ConfigItem.new(key: :team_name,
|
46
|
+
short_option: "-l",
|
47
|
+
env_name: "PRECHECK_TEAM_NAME",
|
48
|
+
description: "The name of your iTunes Connect team if you're in multiple teams",
|
49
|
+
optional: true,
|
50
|
+
default_value: CredentialsManager::AppfileConfig.try_fetch_value(:team_name),
|
51
|
+
verify_block: proc do |value|
|
52
|
+
ENV["FASTLANE_ITC_TEAM_NAME"] = value.to_s
|
53
|
+
end),
|
54
|
+
FastlaneCore::ConfigItem.new(key: :default_rule_level,
|
55
|
+
short_option: "-r",
|
56
|
+
env_name: "PRECHECK_DEFAULT_RULE_LEVEL",
|
57
|
+
description: "The default rule level unless otherwise configured",
|
58
|
+
is_string: false,
|
59
|
+
default_value: RULE_LEVELS[:error])
|
60
|
+
] + rules
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'fastlane_core'
|
2
|
+
require 'precheck/item_to_check'
|
3
|
+
require 'precheck/rule_check_result'
|
4
|
+
|
5
|
+
module Precheck
|
6
|
+
VALIDATION_STATES = {
|
7
|
+
passed: "passed",
|
8
|
+
failed: "failed",
|
9
|
+
skipped: "skipped"
|
10
|
+
}
|
11
|
+
|
12
|
+
# rules can cause warnings, errors, or be skipped all together
|
13
|
+
# by default they are set to indicate a RULE_LEVELS[:error]
|
14
|
+
RULE_LEVELS = {
|
15
|
+
warn: :warn,
|
16
|
+
error: :error,
|
17
|
+
skip: :skip
|
18
|
+
}
|
19
|
+
|
20
|
+
# Abstract super class
|
21
|
+
attr_accessor :rule_block
|
22
|
+
|
23
|
+
class Rule < FastlaneCore::ConfigItem
|
24
|
+
# when a rule evaluates a single item, it has a validation state
|
25
|
+
# if it fails, it has some data like what text failed to pass, or maybe a bad url
|
26
|
+
# this class encapsulates that return value and state
|
27
|
+
# it is the return value from each evaluated @rule_block
|
28
|
+
class RuleReturn
|
29
|
+
attr_accessor :validation_state
|
30
|
+
attr_accessor :failure_data
|
31
|
+
|
32
|
+
def initialize(validation_state: nil, failure_data: nil)
|
33
|
+
@validation_state = validation_state
|
34
|
+
@failure_data = failure_data
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(short_option: nil,
|
39
|
+
verify_block: nil,
|
40
|
+
is_string: nil,
|
41
|
+
type: nil,
|
42
|
+
conflicting_options: nil,
|
43
|
+
conflict_block: nil,
|
44
|
+
deprecated: nil,
|
45
|
+
sensitive: nil,
|
46
|
+
display_in_shell: nil)
|
47
|
+
|
48
|
+
super(key: self.class.key,
|
49
|
+
env_name: self.class.env_name,
|
50
|
+
description: self.class.description,
|
51
|
+
short_option: short_option,
|
52
|
+
default_value: self.class.default_value,
|
53
|
+
verify_block: verify_block,
|
54
|
+
is_string: is_string,
|
55
|
+
type: type,
|
56
|
+
optional: true,
|
57
|
+
conflicting_options: conflicting_options,
|
58
|
+
conflict_block: conflict_block,
|
59
|
+
deprecated: deprecated,
|
60
|
+
sensitive: sensitive,
|
61
|
+
display_in_shell: display_in_shell)
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
@key
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.env_name
|
69
|
+
not_implemented(__method__)
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.key
|
73
|
+
not_implemented(__method__)
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.description
|
77
|
+
not_implemented(__method__)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.friendly_name
|
81
|
+
not_implemented(__method__)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.default_value
|
85
|
+
CredentialsManager::AppfileConfig.try_fetch_value(self.key)
|
86
|
+
end
|
87
|
+
|
88
|
+
def friendly_name
|
89
|
+
return self.class.friendly_name
|
90
|
+
end
|
91
|
+
|
92
|
+
def inspect
|
93
|
+
"#{self.class}(description: #{@description}, key: #{@key})"
|
94
|
+
end
|
95
|
+
|
96
|
+
# some rules can be customized with extra data at runtime, see CustomTextRule as an example
|
97
|
+
def needs_customization?
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
|
101
|
+
# some rules can be customized with extra data at runtime, see CustomTextRule as an example
|
102
|
+
def customize_with_data(data: nil)
|
103
|
+
not_implemented(__method__)
|
104
|
+
end
|
105
|
+
|
106
|
+
# some rules only support specific fields, by default, all fields are supported unless restricted by
|
107
|
+
# providing a list of symbols matching the item_name as defined as the ItemToCheck is generated
|
108
|
+
def supported_fields_symbol_set
|
109
|
+
return nil
|
110
|
+
end
|
111
|
+
|
112
|
+
def rule_block
|
113
|
+
not_implemented(__method__)
|
114
|
+
end
|
115
|
+
|
116
|
+
def check_item(item)
|
117
|
+
# validate the item we have was properly matched to this rule: TextItem -> TextRule, URLItem -> URLRule
|
118
|
+
return skip_item_not_meant_for_this_rule(item) unless handle_item?(item)
|
119
|
+
return skip_item_not_meant_for_this_rule(item) unless item_field_supported?(item_name: item.item_name)
|
120
|
+
|
121
|
+
# do the actual checking now
|
122
|
+
return perform_check(item: item)
|
123
|
+
end
|
124
|
+
|
125
|
+
def skip_item_not_meant_for_this_rule(item)
|
126
|
+
# item isn't mean for this rule, which is fine, we can just keep passing it along
|
127
|
+
return nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# each rule can define what type of ItemToCheck subclass they support
|
131
|
+
# override this method and return true or false
|
132
|
+
def handle_item?(item)
|
133
|
+
not_implemented(__method__)
|
134
|
+
end
|
135
|
+
|
136
|
+
def item_field_supported?(item_name: nil)
|
137
|
+
return true if supported_fields_symbol_set.nil?
|
138
|
+
return true if supported_fields_symbol_set.include?(item_name)
|
139
|
+
return false
|
140
|
+
end
|
141
|
+
|
142
|
+
def perform_check(item: nil)
|
143
|
+
if item.item_data.to_s == "" && item.is_optional
|
144
|
+
# item is optional, and empty, so that's totally fine
|
145
|
+
check_result = RuleReturn.new(validation_state: Precheck::VALIDATION_STATES[:passed])
|
146
|
+
return RuleCheckResult.new(item, check_result, self)
|
147
|
+
end
|
148
|
+
|
149
|
+
check_result = self.rule_block.call(item.item_data)
|
150
|
+
return RuleCheckResult.new(item, check_result, self)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Rule types are more or less just marker classes that are intended to communicate what types of things each rule
|
155
|
+
# expects to deal with. TextRules deal with checking text values, URLRules will check url specific things like connectivity
|
156
|
+
# TextRules expect that text values will be passed to the rule_block, likewise, URLs are expected to be passed to the
|
157
|
+
# URLRule rule_block
|
158
|
+
class TextRule < Rule
|
159
|
+
def handle_item?(item)
|
160
|
+
(item.kind_of? TextItemToCheck) ? true : false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class URLRule < Rule
|
165
|
+
def handle_item?(item)
|
166
|
+
(item.kind_of? URLItemToCheck) ? true : false
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Precheck
|
2
|
+
# after each item is checked for rule conformance to a single rule, a result is generated
|
3
|
+
# a result can have a status of success or fail for a given rule/item
|
4
|
+
class RuleCheckResult
|
5
|
+
attr_accessor :item
|
6
|
+
attr_accessor :rule_return # RuleReturn
|
7
|
+
attr_accessor :rule
|
8
|
+
|
9
|
+
def initialize(item, rule_return, rule)
|
10
|
+
@item = item
|
11
|
+
@rule_return = rule_return
|
12
|
+
@rule = rule
|
13
|
+
end
|
14
|
+
|
15
|
+
def status
|
16
|
+
rule_return.validation_state
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
require 'spaceship'
|
2
|
+
require 'fastlane/markdown_table_formatter'
|
3
|
+
require 'precheck/item_to_check'
|
4
|
+
|
5
|
+
module Precheck
|
6
|
+
# encapsulated the results of the rule processing, needed to return not just an array of the results of our
|
7
|
+
# checks, but also an array of items we didn't check, just in-case we were expecting to check everything
|
8
|
+
class RuleProcessResult
|
9
|
+
attr_accessor :error_results # { rule: [result, result, ...] }
|
10
|
+
attr_accessor :warning_results # { rule: [result, result, ...] }
|
11
|
+
attr_accessor :skipped_rules
|
12
|
+
attr_accessor :items_not_checked
|
13
|
+
|
14
|
+
def initialize(error_results: nil,
|
15
|
+
warning_results: nil,
|
16
|
+
skipped_rules: nil,
|
17
|
+
items_not_checked: nil)
|
18
|
+
@error_results = error_results
|
19
|
+
@warning_results = warning_results
|
20
|
+
@skipped_rules = skipped_rules
|
21
|
+
@items_not_checked = items_not_checked
|
22
|
+
end
|
23
|
+
|
24
|
+
def should_trigger_user_error?
|
25
|
+
return true if error_results.length > 0
|
26
|
+
return false
|
27
|
+
end
|
28
|
+
|
29
|
+
def has_errors_or_warnings?
|
30
|
+
return true if error_results.length > 0 || warning_results.length > 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def items_not_checked?
|
34
|
+
return true if items_not_checked.length > 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class RuleProcessor
|
39
|
+
def self.process_app_and_version(app: nil, app_version: nil, rules: nil)
|
40
|
+
items_to_check = []
|
41
|
+
items_to_check += generate_text_items_to_check(app: app, app_version: app_version)
|
42
|
+
items_to_check += generate_url_items_to_check(app: app, app_version: app_version)
|
43
|
+
|
44
|
+
return process_rules(items_to_check: items_to_check, rules: rules)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.process_rules(items_to_check: nil, rules: nil)
|
48
|
+
items_not_checked = items_to_check.to_set # items we haven't checked by at least one rule
|
49
|
+
error_results = {} # rule to fields map
|
50
|
+
warning_results = {} # rule to fields map
|
51
|
+
skipped_rules = []
|
52
|
+
|
53
|
+
rules.each do |rule|
|
54
|
+
rule_config = Precheck.config[rule.key]
|
55
|
+
rule_level = rule_config[:level].to_sym unless rule_config.nil?
|
56
|
+
rule_level ||= Precheck.config[:default_rule_level]
|
57
|
+
|
58
|
+
if rule_level == RULE_LEVELS[:skip]
|
59
|
+
skipped_rules << rule
|
60
|
+
UI.message "Skipped: #{rule.class.friendly_name}-> #{rule.description}".yellow
|
61
|
+
next
|
62
|
+
end
|
63
|
+
|
64
|
+
if rule.needs_customization?
|
65
|
+
if rule_config.nil? || rule_config[:data].nil?
|
66
|
+
UI.verbose("#{rule.key} excluded because no data was passed to it e.g.: #{rule.key}(data: <data here>)")
|
67
|
+
next
|
68
|
+
end
|
69
|
+
|
70
|
+
custom_data = rule_config[:data]
|
71
|
+
rule.customize_with_data(data: custom_data)
|
72
|
+
end
|
73
|
+
|
74
|
+
# if the rule failed at least once, we won't print a success message
|
75
|
+
rule_failed_at_least_once = false
|
76
|
+
|
77
|
+
items_to_check.each do |item|
|
78
|
+
result = rule.check_item(item)
|
79
|
+
|
80
|
+
# each rule will determine if it can handle this item, if not, it will just pass nil back
|
81
|
+
next if result.nil?
|
82
|
+
|
83
|
+
# we've checked this item, remove it from list of items not checked
|
84
|
+
items_not_checked.delete(item)
|
85
|
+
|
86
|
+
# if we passed, then go to the next item, otherwise, recode the failure
|
87
|
+
next unless result.status == VALIDATION_STATES[:failed]
|
88
|
+
add_new_result_to_rule_hash(rule_hash: error_results, result: result) if rule_level == RULE_LEVELS[:error]
|
89
|
+
add_new_result_to_rule_hash(rule_hash: warning_results, result: result) if rule_level == RULE_LEVELS[:warn]
|
90
|
+
rule_failed_at_least_once = true
|
91
|
+
end
|
92
|
+
|
93
|
+
if rule_failed_at_least_once
|
94
|
+
UI.error "😵 Failed: #{rule.class.friendly_name}-> #{rule.description}"
|
95
|
+
else
|
96
|
+
UI.message "✅ Passed: #{rule.class.friendly_name}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
return RuleProcessResult.new(
|
101
|
+
error_results: error_results,
|
102
|
+
warning_results: warning_results,
|
103
|
+
skipped_rules: skipped_rules,
|
104
|
+
items_not_checked: items_not_checked.to_a
|
105
|
+
)
|
106
|
+
end
|
107
|
+
|
108
|
+
# hash will be { rule: [result, result, result] }
|
109
|
+
def self.add_new_result_to_rule_hash(rule_hash: nil, result: nil)
|
110
|
+
result_array = rule_hash[result.rule] ||= []
|
111
|
+
result_array << result
|
112
|
+
rule_hash[result.rule] = result_array
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.generate_url_items_to_check(app: nil, app_version: nil)
|
116
|
+
items = []
|
117
|
+
items += collect_urls_from_hash(hash: app_version.support_url,
|
118
|
+
item_name: :support_url,
|
119
|
+
friendly_name_postfix: "support URL")
|
120
|
+
items += collect_urls_from_hash(hash: app_version.marketing_url,
|
121
|
+
item_name: :marketing_url,
|
122
|
+
friendly_name_postfix: "marketing URL",
|
123
|
+
is_optional: true)
|
124
|
+
|
125
|
+
items += collect_urls_from_hash(hash: app.details.privacy_url,
|
126
|
+
item_name: :privacy_url,
|
127
|
+
friendly_name_postfix: "privacy URL",
|
128
|
+
is_optional: true)
|
129
|
+
return items
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.collect_urls_from_hash(hash: nil, item_name: nil, friendly_name_postfix: nil, is_optional: false)
|
133
|
+
items = []
|
134
|
+
hash.each do |key, value|
|
135
|
+
items << URLItemToCheck.new(value, item_name, "#{friendly_name_postfix}: (#{key})", is_optional)
|
136
|
+
end
|
137
|
+
return items
|
138
|
+
end
|
139
|
+
|
140
|
+
def self.generate_text_items_to_check(app: nil, app_version: nil)
|
141
|
+
items = []
|
142
|
+
items << TextItemToCheck.new(app_version.copyright, :copyright, "copyright")
|
143
|
+
items << TextItemToCheck.new(app_version.review_first_name, :review_first_name, "review first name")
|
144
|
+
items << TextItemToCheck.new(app_version.review_last_name, :review_last_name, "review last name")
|
145
|
+
items << TextItemToCheck.new(app_version.review_phone_number, :review_phone_number, "review phone number")
|
146
|
+
items << TextItemToCheck.new(app_version.review_email, :review_email, "review email")
|
147
|
+
items << TextItemToCheck.new(app_version.review_demo_user, :review_demo_user, "review demo user")
|
148
|
+
items << TextItemToCheck.new(app_version.review_notes, :review_notes, "review notes")
|
149
|
+
|
150
|
+
items += collect_text_items_from_language_item(hash: app_version.keywords,
|
151
|
+
item_name: :keywords,
|
152
|
+
friendly_name_postfix: "keywords")
|
153
|
+
|
154
|
+
items += collect_text_items_from_language_item(hash: app_version.description,
|
155
|
+
item_name: :description,
|
156
|
+
friendly_name_postfix: "description")
|
157
|
+
|
158
|
+
items += collect_text_items_from_language_item(hash: app_version.release_notes,
|
159
|
+
item_name: :release_notes,
|
160
|
+
friendly_name_postfix: "release notes")
|
161
|
+
|
162
|
+
items += collect_text_items_from_language_item(hash: app.details.name,
|
163
|
+
item_name: :app_name,
|
164
|
+
friendly_name_postfix: "app name")
|
165
|
+
|
166
|
+
items += collect_text_items_from_language_item(hash: app.details.apple_tv_privacy_policy,
|
167
|
+
item_name: :app_subtitle,
|
168
|
+
friendly_name_postfix: " tv privacy policy")
|
169
|
+
|
170
|
+
items += collect_text_items_from_language_item(hash: app.details.subtitle,
|
171
|
+
item_name: :app_subtitle,
|
172
|
+
friendly_name_postfix: "app name subtitle",
|
173
|
+
is_optional: true)
|
174
|
+
return items
|
175
|
+
end
|
176
|
+
|
177
|
+
# # a few attributes are LanguageItem this method creates a TextItemToCheck for each pair
|
178
|
+
def self.collect_text_items_from_language_item(hash: nil, item_name: nil, friendly_name_postfix: nil, is_optional: false)
|
179
|
+
items = []
|
180
|
+
hash.each do |key, value|
|
181
|
+
items << TextItemToCheck.new(value, item_name, "#{friendly_name_postfix}: (#{key})", is_optional)
|
182
|
+
end
|
183
|
+
return items
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# we want to get some of the same behavior hashes has, so use this mixin specifically designed for Spaceship::Tunes::LanguageItem
|
189
|
+
# because we use .each
|
190
|
+
module LanguageItemHashBehavior
|
191
|
+
# this is used to create a hash-like .each method.
|
192
|
+
def each(&block)
|
193
|
+
keys.each { |key| yield(key, get_value(key: key)) }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
class Spaceship::Tunes::LanguageItem
|
198
|
+
include LanguageItemHashBehavior
|
199
|
+
end
|