travis-yaml 0.1.0 → 0.2.0

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -8
  3. data/Gemfile +3 -6
  4. data/Gemfile.lock +20 -50
  5. data/README.md +97 -2
  6. data/SPEC.md +116 -33
  7. data/lib/travis/yaml.rb +6 -3
  8. data/lib/travis/yaml/matrix.rb +12 -17
  9. data/lib/travis/yaml/nodes.rb +4 -0
  10. data/lib/travis/yaml/nodes/addons.rb +44 -0
  11. data/lib/travis/yaml/nodes/android.rb +7 -0
  12. data/lib/travis/yaml/nodes/cache.rb +5 -3
  13. data/lib/travis/yaml/nodes/deploy_conditions.rb +2 -1
  14. data/lib/travis/yaml/nodes/deploy_entry.rb +46 -0
  15. data/lib/travis/yaml/nodes/dist.rb +6 -0
  16. data/lib/travis/yaml/nodes/env.rb +1 -1
  17. data/lib/travis/yaml/nodes/group.rb +6 -0
  18. data/lib/travis/yaml/nodes/language.rb +3 -2
  19. data/lib/travis/yaml/nodes/language_specific.rb +2 -2
  20. data/lib/travis/yaml/nodes/mapping.rb +39 -10
  21. data/lib/travis/yaml/nodes/node.rb +58 -1
  22. data/lib/travis/yaml/nodes/notifications.rb +10 -6
  23. data/lib/travis/yaml/nodes/open_mapping.rb +1 -1
  24. data/lib/travis/yaml/nodes/os.rb +1 -1
  25. data/lib/travis/yaml/nodes/os_entry.rb +7 -5
  26. data/lib/travis/yaml/nodes/root.rb +28 -4
  27. data/lib/travis/yaml/nodes/scalar.rb +16 -1
  28. data/lib/travis/yaml/nodes/sequence.rb +38 -0
  29. data/lib/travis/yaml/nodes/version.rb +13 -0
  30. data/lib/travis/yaml/nodes/version_list.rb +4 -0
  31. data/lib/travis/yaml/parser/psych.rb +11 -5
  32. data/lib/travis/yaml/secure_string.rb +35 -2
  33. data/lib/travis/yaml/serializer.rb +17 -0
  34. data/lib/travis/yaml/serializer/generic.rb +114 -0
  35. data/lib/travis/yaml/serializer/json.rb +72 -0
  36. data/lib/travis/yaml/serializer/legacy.rb +32 -0
  37. data/lib/travis/yaml/serializer/ruby.rb +13 -0
  38. data/lib/travis/yaml/serializer/yaml.rb +41 -0
  39. data/lib/travis/yaml/version.rb +1 -1
  40. data/play/spec.rb +24 -17
  41. data/spec/matrix_spec.rb +57 -0
  42. data/spec/nodes/addons_spec.rb +63 -0
  43. data/spec/nodes/cache_spec.rb +4 -4
  44. data/spec/nodes/deploy_spec.rb +12 -0
  45. data/spec/nodes/dist_spec.rb +11 -0
  46. data/spec/nodes/env_spec.rb +48 -0
  47. data/spec/nodes/git_spec.rb +1 -1
  48. data/spec/nodes/group_spec.rb +11 -0
  49. data/spec/nodes/node_js_spec.rb +14 -0
  50. data/spec/nodes/notifications_spec.rb +7 -0
  51. data/spec/nodes/os_spec.rb +13 -0
  52. data/spec/nodes/root_spec.rb +36 -0
  53. data/spec/nodes/secure_spec.rb +145 -0
  54. data/spec/parser/psych_spec.rb +6 -0
  55. data/spec/parser/ruby_spec.rb +1 -1
  56. data/spec/serializer/json_spec.rb +30 -0
  57. data/spec/serializer/legacy_spec.rb +47 -0
  58. data/spec/serializer/ruby_spec.rb +21 -0
  59. data/spec/serializer/yaml_spec.rb +47 -0
  60. data/spec/support/coverage.rb +9 -9
  61. data/spec/yaml_spec.rb +23 -0
  62. data/travis-yaml.gemspec +2 -3
  63. metadata +42 -22
  64. data/config.ru +0 -2
  65. data/play/weblint.rb +0 -296
@@ -11,7 +11,7 @@ module Travis::Yaml
11
11
  end
12
12
 
13
13
  def accept_key?(key)
14
- true
14
+ key != 'secure'
15
15
  end
16
16
  end
17
17
  end
@@ -5,7 +5,7 @@ module Travis::Yaml
5
5
 
6
6
  def verify_language(language)
7
7
  children.delete_if do |os|
8
- next false if os.supports_language? language
8
+ next false if os.supports_language? language.to_s
9
9
  warning "dropping %p, does not support %p", os, language
10
10
  true
11
11
  end
@@ -1,7 +1,11 @@
1
1
  module Travis::Yaml
2
2
  module Nodes
3
3
  class OSEntry < FixedValue
4
- OSX = %w[objective-c ruby c cpp]
4
+ MISSING = {
5
+ # see https://github.com/travis-ci/travis-ci/issues/2320
6
+ 'osx' => %w[node_js python php perl erlang groovy clojure scala go haskell],
7
+ 'linux' => ['objective-c']
8
+ }
5
9
 
6
10
  ignore_case
7
11
  default :linux
@@ -9,10 +13,8 @@ module Travis::Yaml
9
13
  value :osx, mac: :osx, macos: :osx
10
14
 
11
15
  def supports_language?(language)
12
- case value
13
- when 'linux' then language != 'objective-c'
14
- when 'osx' then OSX.include? language
15
- end
16
+ return false unless missing = MISSING[value]
17
+ !missing.include?(language)
16
18
  end
17
19
  end
18
20
  end
@@ -4,9 +4,12 @@ module Travis::Yaml
4
4
  include LanguageSpecific
5
5
 
6
6
  map :language, required: true
7
+ map :sudo, to: Scalar[:bool], required: false
7
8
  map :bundler_args, to: BundlerArgs
8
- map :deploy, :ruby, :os, :compiler, :git, :jdk, :virtualenv, :matrix, :env, :notifications, :branches, :cache
9
- map :lein, :otp_release, :go, :ghc, :node_js, :xcode_sdk, :xcode_scheme, :perl, :php, :python, :services, :gemfile, to: VersionList
9
+ map :deploy, :ruby, :os, :compiler, :git, :jdk, :virtualenv, :matrix, :env, :notifications, :branches, :cache, :addons, :android
10
+ map :lein, :otp_release, :go, :ghc, :xcode_sdk, :xcode_scheme, :perl, :php, :python, :services, :gemfile, to: VersionList
11
+ map :podfile, to: Version
12
+ map :node_js, to: VersionList[/^\d+\.\d+(\.\d+)?$/]
10
13
  map :rvm, to: :ruby
11
14
  map :otp, to: :otp_release
12
15
  map :node, to: :node_js
@@ -14,9 +17,12 @@ module Travis::Yaml
14
17
  map :osx_image, to: Version, experimental: true
15
18
  map :gobuild_args, :xcode_project, :xcode_workspace, :xctool_args, :composer_args, :npm_args, to: Scalar[:str]
16
19
  map :source_key, to: Scalar[:str, :secure]
17
- map :sdk_components, to: Sequence
18
20
  map :before_install, :install, :before_script, :script, :after_result, :after_script,
19
21
  :after_success, :after_failure, :before_deploy, :after_deploy, to: Stage
22
+ map :dist, to: Dist
23
+ map :group, to: Group
24
+
25
+ FEATURE_KEYS = [:dist, :group]
20
26
 
21
27
  def initialize
22
28
  super(nil)
@@ -26,10 +32,24 @@ module Travis::Yaml
26
32
  super
27
33
  verify_os
28
34
  verify_language(language)
35
+ FEATURE_KEYS.each {|feature| warn_on_feature feature}
29
36
  end
30
37
 
31
38
  def verify_os
32
39
  self.os = language.default_os unless include? :os
40
+ warning 'your repository must be feature flagged for the "os" setting to be used' if os and os != language.default_os
41
+
42
+ if os.include? 'osx' and jdk
43
+ # https://github.com/travis-ci/travis-ci/issues/2317
44
+ warning 'dropping "jdk" section: currently not supported on "osx"'
45
+ @mapping.delete('jdk')
46
+ end
47
+ end
48
+
49
+ def warn_on_feature(feature)
50
+ if include? feature
51
+ warning 'your repository must be feature flagged for the "%s" setting to be used', feature
52
+ end
33
53
  end
34
54
 
35
55
  def nested_warnings(*)
@@ -39,6 +59,10 @@ module Travis::Yaml
39
59
  def inspect
40
60
  "#<Travis::Yaml:#{super}>"
41
61
  end
62
+
63
+ def to_matrix
64
+ Travis::Yaml.matrix(self)
65
+ end
42
66
  end
43
67
  end
44
- end
68
+ end
@@ -82,7 +82,7 @@ module Travis::Yaml
82
82
  def cast(visitor, type, value)
83
83
  visitor.cast(type, value)
84
84
  rescue ArgumentError => error
85
- error "failed to parse %p - %s", type.to_s, error.message
85
+ error "failed to parse %p - %s", type.to_s, error.message.sub("():", ":")
86
86
  end
87
87
 
88
88
  def cast?(type)
@@ -92,6 +92,21 @@ module Travis::Yaml
92
92
  def !@
93
93
  !value
94
94
  end
95
+
96
+ def with_value(value)
97
+ return value.dup if value.is_a? self.class
98
+ value = value.value while value.is_a? Scalar
99
+ super(value)
100
+ end
101
+
102
+ def with_value!(value)
103
+ self.value = value
104
+ end
105
+
106
+ def each_scalar(type = nil, &block)
107
+ return enum_for(:each_scalar, type) unless block
108
+ yield value if type.nil? or type === value
109
+ end
95
110
  end
96
111
  end
97
112
  end
@@ -4,6 +4,11 @@ module Travis::Yaml
4
4
  attr_reader :children
5
5
  alias_method :__getobj__, :children
6
6
 
7
+ def self.[](node_type)
8
+ node_type = Scalar[node_type] unless node_type.is_a? Node
9
+ Class.new(self) { type(node_type) }
10
+ end
11
+
7
12
  def self.type(identifier = nil)
8
13
  @type = Nodes[identifier] if identifier
9
14
  @type ||= superclass.respond_to?(:type) ? superclass.type : Scalar
@@ -79,6 +84,39 @@ module Travis::Yaml
79
84
  @children.each(&:deep_verify)
80
85
  super
81
86
  end
87
+
88
+ def each_scalar(type = nil, &block)
89
+ return enum_for(:each_scalar, type) unless block
90
+ @children.each { |c| c.each_scalar(type, &block) }
91
+ end
92
+
93
+ def with_value(value)
94
+ return value.dup if value.is_a? self.class
95
+ value = value.children if value.is_a? Sequence
96
+ value = value.value while value.is_a? Scalar
97
+ Parser::Ruby.new(Array(value)).parse self.class.new(parent)
98
+ end
99
+
100
+ def with_value!(value)
101
+ children.replace with_value(value).children
102
+ end
103
+
104
+ def add_value(value)
105
+ added = with_value(self)
106
+ added.add_value!(value)
107
+ added
108
+ end
109
+
110
+ def add_value!(value)
111
+ children.concat(with_value(value).children)
112
+ end
113
+
114
+ protected
115
+
116
+ def dup_values
117
+ @children = @children.map { |child| child.dup }
118
+ self
119
+ end
82
120
  end
83
121
  end
84
122
  end
@@ -1,6 +1,19 @@
1
1
  module Travis::Yaml
2
2
  module Nodes
3
3
  class Version < Scalar
4
+ def self.[](expression)
5
+ Class.new(self) { format(expression) }
6
+ end
7
+
8
+ def self.format(expression = nil)
9
+ @format = expression if expression
10
+ @format ||= superclass.respond_to?(:format) ? superclass.format : //
11
+ end
12
+
13
+ def value=(value)
14
+ return super unless value and value.to_s !~ self.class.format
15
+ error "value %p is not a valid version", value.to_s
16
+ end
4
17
  end
5
18
  end
6
19
  end
@@ -1,6 +1,10 @@
1
1
  module Travis::Yaml
2
2
  module Nodes
3
3
  class VersionList < Sequence
4
+ def self.[](expression)
5
+ Class.new(self) { type Version[expression] }
6
+ end
7
+
4
8
  type Version
5
9
  end
6
10
  end
@@ -119,7 +119,13 @@ module Travis::Yaml
119
119
  when SEQ then node.visit_sequence self, value
120
120
  when nil
121
121
  if value.children.size == 2 and value.children.first.value == 'secure'
122
- node.visit_scalar(self, :secure, value.children.last)
122
+ secret_value = value.children.last
123
+ if secret_value.is_a? ::Psych::Nodes::Scalar
124
+ secret_value.tag ||= '!secure'
125
+ node.visit_scalar(self, :secure, secret_value, false)
126
+ else
127
+ node.visit_unexpected(self, value, "secret value needs to be a string")
128
+ end
123
129
  else
124
130
  node.visit_mapping(self, value)
125
131
  end
@@ -205,12 +211,12 @@ module Travis::Yaml
205
211
  end
206
212
 
207
213
  def generate_key(node, value)
208
- unless value.respond_to? :value and (value.tag.nil? || value.tag == STR)
214
+ if value.respond_to? :value and (value.tag.nil? || value.tag == STR)
215
+ value = value.value.to_s
216
+ value.start_with?(?:) ? value[1..-1] : value
217
+ else
209
218
  node.visit_unexpected(self, value, "expected string as key")
210
219
  end
211
-
212
- value = value.value.to_s
213
- value.start_with?(?:) ? value[1..-1] : value
214
220
  end
215
221
  end
216
222
  end
@@ -2,11 +2,44 @@ module Travis::Yaml
2
2
  class SecureString
3
3
  attr_accessor :encrypted_string, :decrypted_string
4
4
  def initialize(string, encrypted = true)
5
+ unless string.respond_to? :to_str
6
+ raise ArgumentError, "secure string needs to be a string, not a %p" % string.class.name.downcase
7
+ end
5
8
  if encrypted
6
- @encryped_string = string
9
+ @encrypted_string = string.to_str
7
10
  else
8
- @decrypted_string = string
11
+ @decrypted_string = string.to_str
9
12
  end
10
13
  end
14
+
15
+ def encrypted?
16
+ !!encrypted_string
17
+ end
18
+
19
+ def decrypted?
20
+ !!decrypted_string
21
+ end
22
+
23
+ def decrypt
24
+ return unless encrypted?
25
+ @decrypted_string = yield(encrypted_string)
26
+ end
27
+
28
+ def encrypt
29
+ return unless decrypted?
30
+ @encrypted_string = yield(decrypted_string)
31
+ end
32
+
33
+ def inspect
34
+ "[SECURE]".freeze
35
+ end
36
+
37
+ def ==(other)
38
+ other.encrypted_string == encrypted_string and other.decrypted_string == decrypted_string if other.is_a? SecureString
39
+ end
40
+
41
+ def hash
42
+ encrypted_string.hash | decrypted_string.hash
43
+ end
11
44
  end
12
45
  end
@@ -0,0 +1,17 @@
1
+ module Travis::Yaml
2
+ module Serializer
3
+ NotSupportedError ||= Class.new(ArgumentError)
4
+ require 'travis/yaml/serializer/generic'
5
+ require 'travis/yaml/serializer/ruby'
6
+ require 'travis/yaml/serializer/legacy'
7
+ require 'travis/yaml/serializer/json'
8
+ require 'travis/yaml/serializer/yaml'
9
+
10
+ def self.[](key)
11
+ return key if key.respond_to? :serialize
12
+ name = constants.detect { |c| c.downcase == key }
13
+ raise ArgumentError, "unknown serializer %p" % key unless name
14
+ const_get(name)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,114 @@
1
+ module Travis::Yaml
2
+ module Serializer
3
+ class Generic
4
+ attr_reader :options
5
+
6
+ def self.serialize(node, options = nil)
7
+ new(options).serialize(node)
8
+ end
9
+
10
+ def initialize(options = nil)
11
+ @options = options || {}
12
+ end
13
+
14
+ def symbol_keys?
15
+ !!options[:symbol_keys]
16
+ end
17
+
18
+ def serialize(node)
19
+ case node
20
+ when Nodes::Root then serialize_root(node)
21
+ when Nodes::Scalar then serialize_scalar(node)
22
+ when Nodes::Mapping then serialize_mapping(node)
23
+ when Nodes::Sequence then serialize_sequence(node)
24
+ else raise NotSupportedError, 'do not know how to serialize %p' % node.class
25
+ end
26
+ end
27
+
28
+ def serialize_scalar(node)
29
+ case value = node.value
30
+ when true, false then serialize_bool(value)
31
+ when Float then serialize_float(value)
32
+ when Integer then serialize_integer(value)
33
+ when Time then serialize_time(value)
34
+ when SecureString then serialize_secure(value)
35
+ when Regexp then serialize_regexp(value)
36
+ when String
37
+ value.encoding == Encoding::BINARY ? serialize_binary(value) : serialize_str(value)
38
+ else
39
+ serialize_value(node)
40
+ end
41
+ end
42
+
43
+ def serialize_bool(value)
44
+ serialize_value(value)
45
+ end
46
+
47
+ def serialize_float(value)
48
+ serialize_value(value)
49
+ end
50
+
51
+ def serialize_time(value)
52
+ serialize_value(value)
53
+ end
54
+
55
+ def serialize_secure(value)
56
+ case options[:secure]
57
+ when :decrypted
58
+ raise ArgumentError, 'secure option is set decrypted, but a secure value is not decrypted' unless value.decrypted?
59
+ serialize_decrypted(value)
60
+ when :encrypted
61
+ raise ArgumentError, 'secure option is set encrypted, but a secure value is not encrypted' unless value.encrypted?
62
+ serialize_encrypted(value)
63
+ else
64
+ raise ArgumentError, 'unexpected value for secure option: %p' % options[:secure] if options[:secure]
65
+ value.encrypted? ? serialize_encrypted(value) : serialize_decrypted(value)
66
+ end
67
+ end
68
+
69
+ def serialize_encrypted(value)
70
+ serialize_value(value)
71
+ end
72
+
73
+ def serialize_decrypted(value)
74
+ serialize_value(value)
75
+ end
76
+
77
+ def serialize_regexp(value)
78
+ serialize_value(value)
79
+ end
80
+
81
+ def serialize_str(value)
82
+ serialize_value(value)
83
+ end
84
+
85
+ def serialize_binary(value)
86
+ serialize_str(value)
87
+ end
88
+
89
+ def serialize_bool(value)
90
+ serialize_value(value)
91
+ end
92
+
93
+ def serialize_value(value)
94
+ raise NotSupportedError, 'cannot serialize %p with %p' % [node.class, self.class]
95
+ end
96
+
97
+ def serialize_root(node)
98
+ serialize_mapping(node)
99
+ end
100
+
101
+ def serialize_mapping(node)
102
+ node.map { |key, value| [serialize_key(key), serialize(value)] }
103
+ end
104
+
105
+ def serialize_sequence(node)
106
+ node.map { |value| serialize(value) }
107
+ end
108
+
109
+ def serialize_key(value)
110
+ symbol_keys? ? value.to_sym : value.to_s
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,72 @@
1
+ module Travis::Yaml
2
+ module Serializer
3
+ class Json < Generic
4
+ MAP = { # mapping stolen from json gem
5
+ "\x0" => '\u0000', "\x1" => '\u0001', "\x2" => '\u0002', "\x3" => '\u0003', "\x4" => '\u0004', "\x5" => '\u0005',
6
+ "\x6" => '\u0006', "\x7" => '\u0007', "\b" => '\b', "\t" => '\t', "\n" => '\n', "\xb" => '\u000b',
7
+ "\f" => '\f', "\r" => '\r', "\xe" => '\u000e', "\xf" => '\u000f', "\x10" => '\u0010', "\x11" => '\u0011',
8
+ "\x12" => '\u0012', "\x13" => '\u0013', "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016', "\x17" => '\u0017',
9
+ "\x18" => '\u0018', "\x19" => '\u0019', "\x1a" => '\u001a', "\x1b" => '\u001b', "\x1c" => '\u001c', "\x1d" => '\u001d',
10
+ "\x1e" => '\u001e', "\x1f" => '\u001f', '"' => '\"', '\\' => '\\\\'
11
+ }
12
+
13
+ def pretty?
14
+ !!options[:pretty]
15
+ end
16
+
17
+ def serialize_float(value)
18
+ raise NotSupportedError, 'cannot serialize infinity as JSON' if value.infinite?
19
+ "#{value}"
20
+ end
21
+
22
+ def serialize_encrypted(value)
23
+ key_value("secure", serialize_str(value.encrypted_string), "{%s}")
24
+ end
25
+
26
+ def serialize_decrypted(value)
27
+ serialize_str(value.decrypted_string)
28
+ end
29
+
30
+ def serialize_str(value)
31
+ string = value.encode('utf-8')
32
+ string.force_encoding('binary')
33
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
34
+ string.force_encoding('utf-8')
35
+ "\"#{string}\""
36
+ end
37
+
38
+ def serialize_binary(value)
39
+ raise NotSupportedError, 'cannot serialize binary data as JSON'
40
+ end
41
+
42
+ def serialize_bool(value)
43
+ value ? "true" : "false"
44
+ end
45
+
46
+ def serialize_mapping(node)
47
+ lines('{%s}', super.map { |key, value| key_value(key, value) })
48
+ end
49
+
50
+ def serialize_sequence(node)
51
+ lines('[%s]', super)
52
+ end
53
+
54
+ def key_value(key, value, wrapper = "%s")
55
+ space = pretty? ? " " : ""
56
+ wrapper % "#{serialize_str(key)}:#{space}#{value}"
57
+ end
58
+
59
+ def lines(wrapper, lines)
60
+ return wrapper % lines.join(',') unless pretty?
61
+ return wrapper % "" if lines.empty?
62
+ return wrapper % " #{lines.first} " unless lines.size > 1 or lines.first.include?("\n") or lines.first.size > 50
63
+ lines = "\n " + lines.join(",\n").strip.gsub("\n", "\n ") + "\n"
64
+ wrapper % lines
65
+ end
66
+
67
+ def serialize_key(value)
68
+ value.to_s
69
+ end
70
+ end
71
+ end
72
+ end