zendesk_apps_support 4.29.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +176 -0
  3. data/README.md +39 -0
  4. data/config/locales/en.yml +163 -0
  5. data/config/locales/translations/zendesk_apps_support.yml +405 -0
  6. data/lib/zendesk_apps_support.rb +42 -0
  7. data/lib/zendesk_apps_support/app_file.rb +44 -0
  8. data/lib/zendesk_apps_support/app_requirement.rb +11 -0
  9. data/lib/zendesk_apps_support/app_version.rb +78 -0
  10. data/lib/zendesk_apps_support/assets/default_app_logo.svg +10 -0
  11. data/lib/zendesk_apps_support/assets/default_styles.scss +3 -0
  12. data/lib/zendesk_apps_support/assets/default_template.html.erb +10 -0
  13. data/lib/zendesk_apps_support/assets/installed.js.erb +21 -0
  14. data/lib/zendesk_apps_support/assets/src.js.erb +37 -0
  15. data/lib/zendesk_apps_support/build_translation.rb +51 -0
  16. data/lib/zendesk_apps_support/engine.rb +11 -0
  17. data/lib/zendesk_apps_support/finders.rb +36 -0
  18. data/lib/zendesk_apps_support/i18n.rb +31 -0
  19. data/lib/zendesk_apps_support/installation.rb +22 -0
  20. data/lib/zendesk_apps_support/installed.rb +27 -0
  21. data/lib/zendesk_apps_support/location.rb +71 -0
  22. data/lib/zendesk_apps_support/manifest.rb +197 -0
  23. data/lib/zendesk_apps_support/manifest/location_options.rb +35 -0
  24. data/lib/zendesk_apps_support/manifest/no_override_hash.rb +50 -0
  25. data/lib/zendesk_apps_support/manifest/parameter.rb +23 -0
  26. data/lib/zendesk_apps_support/package.rb +402 -0
  27. data/lib/zendesk_apps_support/product.rb +31 -0
  28. data/lib/zendesk_apps_support/sass_functions.rb +43 -0
  29. data/lib/zendesk_apps_support/stylesheet_compiler.rb +38 -0
  30. data/lib/zendesk_apps_support/validations/banner.rb +35 -0
  31. data/lib/zendesk_apps_support/validations/manifest.rb +412 -0
  32. data/lib/zendesk_apps_support/validations/marketplace.rb +31 -0
  33. data/lib/zendesk_apps_support/validations/mime.rb +41 -0
  34. data/lib/zendesk_apps_support/validations/requests.rb +47 -0
  35. data/lib/zendesk_apps_support/validations/requirements.rb +154 -0
  36. data/lib/zendesk_apps_support/validations/secrets.rb +77 -0
  37. data/lib/zendesk_apps_support/validations/secure_settings.rb +37 -0
  38. data/lib/zendesk_apps_support/validations/source.rb +25 -0
  39. data/lib/zendesk_apps_support/validations/stylesheets.rb +28 -0
  40. data/lib/zendesk_apps_support/validations/svg.rb +81 -0
  41. data/lib/zendesk_apps_support/validations/templates.rb +20 -0
  42. data/lib/zendesk_apps_support/validations/translations.rb +160 -0
  43. data/lib/zendesk_apps_support/validations/validation_error.rb +77 -0
  44. metadata +327 -0
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'loofah'
4
+
5
+ module ZendeskAppsSupport
6
+ module Validations
7
+ module Svg
8
+ PLACEHOLDER_SVG_MARKUP = File.read(File.expand_path('../../assets/default_app_logo.svg', __FILE__))
9
+
10
+ # whitelist elements and attributes used in Zendesk Garden assets
11
+ Loofah::HTML5::WhiteList::ALLOWED_ELEMENTS_WITH_LIBXML2.add 'symbol'
12
+ Loofah::HTML5::WhiteList::ACCEPTABLE_CSS_PROPERTIES.add 'position'
13
+
14
+ # CRUFT: ignore a (very specific) style attribute which Loofah would otherwise scrub.
15
+ # This attribute is deprecated (https://www.w3.org/TR/filter-effects/#AccessBackgroundImage)
16
+ # but is included in many of the test apps used in fixtures for tests in ZAM, ZAT etc.
17
+ Loofah::HTML5::WhiteList::ACCEPTABLE_CSS_PROPERTIES.add 'enable-background'
18
+
19
+ @strip_declaration = Loofah::Scrubber.new do |node|
20
+ node.remove if node.name == 'xml' && node.children.empty?
21
+ end
22
+
23
+ # Loofah's default scrubber strips spaces between CSS attributes. Passing the input markup through this scrubber
24
+ # first ensures that this stripped whitespace in the output doesn't register as a diff.
25
+ @strip_spaces_between_css_attrs = Loofah::Scrubber.new do |node|
26
+ match_pattern = Regexp.new(/\;\s+/)
27
+ if node.name == 'svg' && node['style']
28
+ node['style'] = node['style'].gsub(match_pattern, ';')
29
+ end
30
+ end
31
+
32
+ @empty_malformed_markup = Loofah::Scrubber.new do |node|
33
+ node.next.remove while node.name == 'svg' && node.next
34
+ end
35
+
36
+ class << self
37
+ def contains_embedded_bitmap?(markup)
38
+ Nokogiri::XML(markup).css('//image').any?
39
+ end
40
+
41
+ def rewrite_svg(svg, new_markup, package, errors)
42
+ warning_string = if contains_embedded_bitmap?(svg.read)
43
+ 'txt.apps.admin.warning.app_build.bitmap_in_svg'
44
+ else
45
+ 'txt.apps.admin.warning.app_build.sanitised_svg'
46
+ end
47
+
48
+ package.warnings << I18n.t(warning_string, svg: svg.relative_path)
49
+ compressed_new_markup = new_markup.tr("\n", '').squeeze(' ').gsub(/\>\s+\</, '><')
50
+ IO.write(svg.absolute_path, compressed_new_markup)
51
+ rescue
52
+ errors << ValidationError.new(:dirty_svg, svg: svg.relative_path)
53
+ end
54
+
55
+ def call(package)
56
+ errors = []
57
+
58
+ package.svg_files.each do |svg|
59
+ if contains_embedded_bitmap?(svg.read)
60
+ rewrite_svg(svg, PLACEHOLDER_SVG_MARKUP, package, errors)
61
+ else
62
+ markup = Loofah.xml_fragment(svg.read)
63
+ .scrub!(@strip_declaration)
64
+ .scrub!(@strip_spaces_between_css_attrs)
65
+ .to_xml.strip
66
+
67
+ clean_markup = Loofah.xml_fragment(markup)
68
+ .scrub!(:prune)
69
+ .scrub!(@empty_malformed_markup)
70
+ .to_xml
71
+
72
+ next if clean_markup == markup
73
+ rewrite_svg(svg, clean_markup, package, errors)
74
+ end
75
+ end
76
+ errors
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ZendeskAppsSupport
4
+ module Validations
5
+ module Templates
6
+ class <<self
7
+ def call(package)
8
+ errors = []
9
+ package.template_files.each do |template|
10
+ contents = template.read
11
+ if contents =~ /<\s*style\b/
12
+ errors << ValidationError.new(:style_in_template, template: template.relative_path)
13
+ end
14
+ end
15
+ errors
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,160 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module ZendeskAppsSupport
6
+ module Validations
7
+ module Translations
8
+ TRANSLATIONS_PATH = %r{^translations/(.*)\.json$}
9
+ VALID_LOCALE = /^[a-z]{2}(-\w{2,3})?$/
10
+
11
+ class TranslationFormatError < StandardError
12
+ end
13
+
14
+ class << self
15
+ def call(package, opts = {})
16
+ package.files.each_with_object([]) do |file, errors|
17
+ path_match = TRANSLATIONS_PATH.match(file.relative_path)
18
+ next unless path_match
19
+ errors << locale_error(file, path_match[1]) << json_error(file) << format_error(file)
20
+ next unless errors.compact.empty?
21
+ if file.relative_path == 'translations/en.json'
22
+ # rubocop:disable Metrics/LineLength
23
+ errors.push(*validate_marketplace_content(file, package, opts.fetch(:skip_marketplace_translations, false)))
24
+ end
25
+ end.compact
26
+ end
27
+
28
+ private
29
+
30
+ def locale_error(file, locale)
31
+ return nil if VALID_LOCALE =~ locale
32
+ ValidationError.new('translation.invalid_locale', file: file.relative_path)
33
+ end
34
+
35
+ def format_error(file)
36
+ with_valid_json(file) do |json|
37
+ if json['app'] && json['app']['parameters']
38
+ parameters_node = json['app']['parameters']
39
+ parameters_node.keys.map do |param|
40
+ unless parameters_node[param].is_a?(Hash) && parameters_node[param].keys.include?('label')
41
+ return ValidationError.new('translation.missing_required_key_on_leaf',
42
+ file: file.relative_path, missing_key: 'label', leaf: param)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ nil
48
+ end
49
+
50
+ def json_error(file)
51
+ with_valid_json(file) do |json|
52
+ if json['app'] && json['app']['package']
53
+ json['app'].delete('package')
54
+ begin
55
+ validate_translation_format(json)
56
+ return
57
+ rescue TranslationFormatError => e
58
+ ValidationError.new('translation.invalid_format', field: e.message)
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def with_valid_json(file)
65
+ json = JSON.parse(file.read)
66
+ if json.is_a?(Hash)
67
+ yield json
68
+ else
69
+ ValidationError.new('translation.not_json_object', file: file.relative_path)
70
+ end
71
+ rescue JSON::ParserError => e
72
+ ValidationError.new('translation.not_json', file: file.relative_path, errors: e)
73
+ end
74
+
75
+ def validate_marketplace_content(file, package, skip_marketplace_translations)
76
+ errors = []
77
+ json = JSON.parse(file.read)
78
+ product_names = Product::PRODUCTS_AVAILABLE.map(&:name)
79
+ present_product_keys = json['app'].is_a?(Hash) ? json['app'].keys & product_names : []
80
+ skip_marketplace_strings = package.manifest.private? || skip_marketplace_translations
81
+
82
+ if present_product_keys.empty?
83
+ errors << validate_top_level_required_keys(json, file.relative_path, skip_marketplace_strings)
84
+ else
85
+ errors << validate_products_match_manifest_products(present_product_keys, package, file.relative_path)
86
+ errors << validate_products_have_required_keys(
87
+ json,
88
+ present_product_keys,
89
+ file.relative_path,
90
+ skip_marketplace_strings
91
+ )
92
+ end
93
+ errors.compact
94
+ end
95
+
96
+ def validate_top_level_required_keys(json, file_path, skip_marketplace_strings)
97
+ keys = json['app'].is_a?(Hash) ? json['app'].keys : []
98
+ missing_keys = get_missing_keys(keys, skip_marketplace_strings)
99
+ return if missing_keys.empty?
100
+ ValidationError.new(
101
+ 'translation.missing_required_key',
102
+ file: file_path,
103
+ missing_key: missing_keys.join(', ')
104
+ )
105
+ end
106
+
107
+ def validate_products_have_required_keys(json, products, file_path, skip_marketplace_strings)
108
+ products.each do |product|
109
+ missing_keys = get_missing_keys(json['app'][product].keys, skip_marketplace_strings)
110
+ next if missing_keys.empty?
111
+ return ValidationError.new(
112
+ 'translation.missing_required_key_for_product',
113
+ file: file_path,
114
+ product: product.capitalize,
115
+ missing_key: missing_keys.join(', ')
116
+ )
117
+ end
118
+ nil
119
+ end
120
+
121
+ def validate_products_match_manifest_products(products, package, file_path)
122
+ manifest_products = package.manifest.products.map(&:name)
123
+ return if (products - manifest_products).empty?
124
+ ValidationError.new(
125
+ 'translation.products_do_not_match_manifest_products',
126
+ file: file_path,
127
+ translation_products: products.map(&:capitalize).join(', '),
128
+ manifest_products: manifest_products.map(&:capitalize).join(', ')
129
+ )
130
+ end
131
+
132
+ def validate_translation_format(json)
133
+ json.keys.each do |key|
134
+ raise TranslationFormatError, "'#{key}': '#{json[key]}'" unless json[key].is_a? Hash
135
+
136
+ if json[key].keys.sort == BuildTranslation::I18N_KEYS &&
137
+ json[key][BuildTranslation::I18N_TITLE_KEY].class == String &&
138
+ json[key][BuildTranslation::I18N_VALUE_KEY].class == String
139
+ next
140
+ else
141
+ validate_translation_format(json[key])
142
+ end
143
+ end
144
+ end
145
+
146
+ def get_missing_keys(keys, skip_marketplace_strings)
147
+ public_app_keys = %w[name short_description installation_instructions long_description]
148
+ mandatory_keys = skip_marketplace_strings ? ['name'] : public_app_keys
149
+ # since we support description as well as short_description for backwards compatibility,
150
+ # validate keys as if description == short_description
151
+ keys_to_validate = keys.map do |key|
152
+ key == 'description' ? 'short_description' : key
153
+ end
154
+
155
+ mandatory_keys - keys_to_validate
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module ZendeskAppsSupport
6
+ module Validations
7
+ class ValidationError < StandardError
8
+ KEY_PREFIX = 'txt.apps.admin.error.app_build.'
9
+
10
+ class DeserializationError < StandardError
11
+ def initialize(serialized)
12
+ super "Cannot deserialize ValidationError from #{serialized}"
13
+ end
14
+ end
15
+
16
+ class << self
17
+ # Turn a JSON string into a ValidationError.
18
+ def from_json(json)
19
+ hash = JSON.parse(json)
20
+ raise DeserializationError, json unless hash.is_a?(Hash)
21
+ from_hash(hash)
22
+ rescue JSON::ParserError, NameError
23
+ raise DeserializationError, json
24
+ end
25
+
26
+ def from_hash(hash)
27
+ raise DeserializationError, hash unless hash['class']
28
+ klass = constantize(hash['class'])
29
+ raise DeserializationError, hash unless klass <= self
30
+ klass.vivify(hash)
31
+ end
32
+
33
+ # Turn a Hash into a ValidationError. Used within from_json.
34
+ def vivify(hash)
35
+ new(hash['key'], hash['data'])
36
+ end
37
+
38
+ private
39
+
40
+ def constantize(klass)
41
+ klass.to_s.split('::').inject(Object) { |superclass, part| superclass.const_get(part) }
42
+ end
43
+ end
44
+
45
+ attr_reader :key, :data
46
+
47
+ def initialize(key, data = nil)
48
+ @key = key
49
+ @data = symbolize_keys(data || {})
50
+ end
51
+
52
+ def to_s
53
+ ZendeskAppsSupport::I18n.t("#{KEY_PREFIX}#{key}", data)
54
+ end
55
+
56
+ def to_json(*)
57
+ JSON.generate(as_json)
58
+ end
59
+
60
+ def as_json(*)
61
+ {
62
+ 'class' => self.class.to_s,
63
+ 'key' => key,
64
+ 'data' => data
65
+ }
66
+ end
67
+
68
+ private
69
+
70
+ def symbolize_keys(hash)
71
+ hash.each_with_object({}) do |(key, value), result|
72
+ result[key.to_sym] = value
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
metadata ADDED
@@ -0,0 +1,327 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zendesk_apps_support
3
+ version: !ruby/object:Gem::Version
4
+ version: 4.29.4
5
+ platform: ruby
6
+ authors:
7
+ - James A. Rosen
8
+ - Likun Liu
9
+ - Sean Caffery
10
+ - Daniel Ribeiro
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2020-07-22 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: i18n
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sassc
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - "~>"
35
+ - !ruby/object:Gem::Version
36
+ version: 1.11.2
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: 1.11.2
44
+ - !ruby/object:Gem::Dependency
45
+ name: sass
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ - !ruby/object:Gem::Dependency
59
+ name: json
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: image_size
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ - !ruby/object:Gem::Dependency
87
+ name: erubis
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ - !ruby/object:Gem::Dependency
101
+ name: loofah
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - "~>"
105
+ - !ruby/object:Gem::Version
106
+ version: 2.2.3
107
+ type: :runtime
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - "~>"
112
+ - !ruby/object:Gem::Version
113
+ version: 2.2.3
114
+ - !ruby/object:Gem::Dependency
115
+ name: nokogiri
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - "~>"
119
+ - !ruby/object:Gem::Version
120
+ version: 1.8.5
121
+ type: :runtime
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - "~>"
126
+ - !ruby/object:Gem::Version
127
+ version: 1.8.5
128
+ - !ruby/object:Gem::Dependency
129
+ name: rb-inotify
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - '='
133
+ - !ruby/object:Gem::Version
134
+ version: 0.9.10
135
+ type: :runtime
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - '='
140
+ - !ruby/object:Gem::Version
141
+ version: 0.9.10
142
+ - !ruby/object:Gem::Dependency
143
+ name: mimemagic
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: 0.3.3
149
+ type: :runtime
150
+ prerelease: false
151
+ version_requirements: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: 0.3.3
156
+ - !ruby/object:Gem::Dependency
157
+ name: ipaddress_2
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: 0.13.0
163
+ type: :runtime
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: 0.13.0
170
+ - !ruby/object:Gem::Dependency
171
+ name: rspec
172
+ requirement: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - "~>"
175
+ - !ruby/object:Gem::Version
176
+ version: 3.4.0
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - "~>"
182
+ - !ruby/object:Gem::Version
183
+ version: 3.4.0
184
+ - !ruby/object:Gem::Dependency
185
+ name: bump
186
+ requirement: !ruby/object:Gem::Requirement
187
+ requirements:
188
+ - - "~>"
189
+ - !ruby/object:Gem::Version
190
+ version: 0.5.1
191
+ type: :development
192
+ prerelease: false
193
+ version_requirements: !ruby/object:Gem::Requirement
194
+ requirements:
195
+ - - "~>"
196
+ - !ruby/object:Gem::Version
197
+ version: 0.5.1
198
+ - !ruby/object:Gem::Dependency
199
+ name: faker
200
+ requirement: !ruby/object:Gem::Requirement
201
+ requirements:
202
+ - - "~>"
203
+ - !ruby/object:Gem::Version
204
+ version: 1.6.6
205
+ type: :development
206
+ prerelease: false
207
+ version_requirements: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - "~>"
210
+ - !ruby/object:Gem::Version
211
+ version: 1.6.6
212
+ - !ruby/object:Gem::Dependency
213
+ name: rubocop
214
+ requirement: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - "~>"
217
+ - !ruby/object:Gem::Version
218
+ version: 0.49.0
219
+ type: :development
220
+ prerelease: false
221
+ version_requirements: !ruby/object:Gem::Requirement
222
+ requirements:
223
+ - - "~>"
224
+ - !ruby/object:Gem::Version
225
+ version: 0.49.0
226
+ - !ruby/object:Gem::Dependency
227
+ name: byebug
228
+ requirement: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - "~>"
231
+ - !ruby/object:Gem::Version
232
+ version: 9.0.6
233
+ type: :development
234
+ prerelease: false
235
+ version_requirements: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - "~>"
238
+ - !ruby/object:Gem::Version
239
+ version: 9.0.6
240
+ - !ruby/object:Gem::Dependency
241
+ name: bundler
242
+ requirement: !ruby/object:Gem::Requirement
243
+ requirements:
244
+ - - '='
245
+ - !ruby/object:Gem::Version
246
+ version: 1.17.3
247
+ type: :development
248
+ prerelease: false
249
+ version_requirements: !ruby/object:Gem::Requirement
250
+ requirements:
251
+ - - '='
252
+ - !ruby/object:Gem::Version
253
+ version: 1.17.3
254
+ description: Support to help you develop Zendesk Apps.
255
+ email:
256
+ - dev@zendesk.com
257
+ executables: []
258
+ extensions: []
259
+ extra_rdoc_files: []
260
+ files:
261
+ - LICENSE
262
+ - README.md
263
+ - config/locales/en.yml
264
+ - config/locales/translations/zendesk_apps_support.yml
265
+ - lib/zendesk_apps_support.rb
266
+ - lib/zendesk_apps_support/app_file.rb
267
+ - lib/zendesk_apps_support/app_requirement.rb
268
+ - lib/zendesk_apps_support/app_version.rb
269
+ - lib/zendesk_apps_support/assets/default_app_logo.svg
270
+ - lib/zendesk_apps_support/assets/default_styles.scss
271
+ - lib/zendesk_apps_support/assets/default_template.html.erb
272
+ - lib/zendesk_apps_support/assets/installed.js.erb
273
+ - lib/zendesk_apps_support/assets/src.js.erb
274
+ - lib/zendesk_apps_support/build_translation.rb
275
+ - lib/zendesk_apps_support/engine.rb
276
+ - lib/zendesk_apps_support/finders.rb
277
+ - lib/zendesk_apps_support/i18n.rb
278
+ - lib/zendesk_apps_support/installation.rb
279
+ - lib/zendesk_apps_support/installed.rb
280
+ - lib/zendesk_apps_support/location.rb
281
+ - lib/zendesk_apps_support/manifest.rb
282
+ - lib/zendesk_apps_support/manifest/location_options.rb
283
+ - lib/zendesk_apps_support/manifest/no_override_hash.rb
284
+ - lib/zendesk_apps_support/manifest/parameter.rb
285
+ - lib/zendesk_apps_support/package.rb
286
+ - lib/zendesk_apps_support/product.rb
287
+ - lib/zendesk_apps_support/sass_functions.rb
288
+ - lib/zendesk_apps_support/stylesheet_compiler.rb
289
+ - lib/zendesk_apps_support/validations/banner.rb
290
+ - lib/zendesk_apps_support/validations/manifest.rb
291
+ - lib/zendesk_apps_support/validations/marketplace.rb
292
+ - lib/zendesk_apps_support/validations/mime.rb
293
+ - lib/zendesk_apps_support/validations/requests.rb
294
+ - lib/zendesk_apps_support/validations/requirements.rb
295
+ - lib/zendesk_apps_support/validations/secrets.rb
296
+ - lib/zendesk_apps_support/validations/secure_settings.rb
297
+ - lib/zendesk_apps_support/validations/source.rb
298
+ - lib/zendesk_apps_support/validations/stylesheets.rb
299
+ - lib/zendesk_apps_support/validations/svg.rb
300
+ - lib/zendesk_apps_support/validations/templates.rb
301
+ - lib/zendesk_apps_support/validations/translations.rb
302
+ - lib/zendesk_apps_support/validations/validation_error.rb
303
+ homepage: http://github.com/zendesk/zendesk_apps_support
304
+ licenses:
305
+ - Apache License Version 2.0
306
+ metadata: {}
307
+ post_install_message:
308
+ rdoc_options: []
309
+ require_paths:
310
+ - lib
311
+ required_ruby_version: !ruby/object:Gem::Requirement
312
+ requirements:
313
+ - - ">="
314
+ - !ruby/object:Gem::Version
315
+ version: '2.0'
316
+ required_rubygems_version: !ruby/object:Gem::Requirement
317
+ requirements:
318
+ - - ">="
319
+ - !ruby/object:Gem::Version
320
+ version: 1.3.6
321
+ requirements: []
322
+ rubyforge_project:
323
+ rubygems_version: 2.7.10
324
+ signing_key:
325
+ specification_version: 4
326
+ summary: Support to help you develop Zendesk Apps.
327
+ test_files: []