travis-yaml 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -7,17 +7,20 @@ module Travis
7
7
  require 'travis/yaml/nodes'
8
8
  require 'travis/yaml/matrix'
9
9
  require 'travis/yaml/parser'
10
+ require 'travis/yaml/serializer'
10
11
 
11
12
  extend self
12
13
 
13
14
  def parse(value)
14
- Parser.parse(value)
15
+ result = Parser.parse(value)
16
+ yield result if block_given?
17
+ result
15
18
  end
16
19
 
17
20
  alias_method :load, :parse
18
21
 
19
- def parse!(value, file_name = '.travis.yml')
20
- result = parse(value)
22
+ def parse!(value, file_name = '.travis.yml', &block)
23
+ result = parse(value, &block)
21
24
  result.nested_warnings.each do |key, message|
22
25
  warn key.empty? ? "#{file_name}: #{message}" :
23
26
  "#{file_name}: #{key.join(?.)} section - #{message}"
@@ -10,23 +10,18 @@ module Travis::Yaml
10
10
  KEYS = EXPAND_KEYS + [:env]
11
11
 
12
12
  class Entry < DelegateClass(Nodes::Root)
13
- attr_reader :env, :matrix_attributes
14
-
13
+ attr_reader :matrix_attributes
15
14
  def initialize(root, matrix_attributes)
16
- super(root)
17
-
18
- @matrix_attributes = matrix_attributes
19
- @env = Nodes::Env.new(self)
20
- inherited_env = root.env.global if root.env
21
- @env.global = [matrix_attributes[:env], *inherited_env].compact
22
- end
23
-
24
- EXPAND_KEYS.each do |key|
25
- define_method(key) { @matrix_attributes.fetch(key, super()) }
26
- end
27
-
28
- def inspect
29
- "#<#{self.class}: #{matrix_attributes}>"
15
+ @matrix_attributes = matrix_attributes
16
+ normal_attributes = matrix_attributes.select { |key| key != :env }
17
+ generated_root = root.with_value(normal_attributes)
18
+ if matrix_attributes[:env]
19
+ generated_root.env.global = Travis::Yaml::Nodes::Env::List.new(generated_root.env)
20
+ generated_root.env.global.add_value! root.env.global if root.env.global
21
+ generated_root.env.global.add_value! matrix_attributes[:env]
22
+ generated_root.env.mapping.delete "matrix"
23
+ end
24
+ super(generated_root)
30
25
  end
31
26
  end
32
27
 
@@ -53,7 +48,7 @@ module Travis::Yaml
53
48
  m.include.each { |i| entries << Hash[axes.map { |k| [k, i[k]] }] } if m.include
54
49
  end
55
50
  entries.map! { |attributes| Entry.new(root, attributes) }
56
- entries.any? ? entries : [root]
51
+ entries.any? ? entries : [Entry.new(root, {})]
57
52
  end
58
53
  end
59
54
 
@@ -35,6 +35,10 @@ module Travis::Yaml
35
35
  require 'travis/yaml/nodes/notifications'
36
36
  require 'travis/yaml/nodes/branches'
37
37
  require 'travis/yaml/nodes/cache'
38
+ require 'travis/yaml/nodes/addons'
39
+ require 'travis/yaml/nodes/android'
40
+ require 'travis/yaml/nodes/dist'
41
+ require 'travis/yaml/nodes/group'
38
42
  require 'travis/yaml/nodes/root'
39
43
  end
40
44
  end
@@ -0,0 +1,44 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Addons < Mapping
4
+ class Addon < Mapping
5
+ def self.[](*keys)
6
+ Class.new(self) { map(*keys, to: Scalar[:str, :secure])}
7
+ end
8
+
9
+ def visit_scalar(visitor, type, value, implicit = true)
10
+ return super unless type == :bool
11
+ end
12
+ end
13
+
14
+ class CoverityScan < Addon
15
+ class Project < Mapping
16
+ map :name, to: Scalar[:str, :secure], required: true
17
+ end
18
+
19
+ map :project, to: Project
20
+ map :build_script_url, :branch_pattern, :notification_email, :build_command,
21
+ :build_command_prepend, to: Scalar[:str, :secure]
22
+ end
23
+
24
+ class Artifacts < Addon
25
+ map :bucket, to: Scalar[:str, :secure], required: true
26
+ map :key, to: Scalar[:str, :secure], required: true
27
+ map :paths, to: Sequence
28
+ map :secret, to: Scalar[:str, :secure], required: true
29
+
30
+ map :branch, :log_format, :target_paths, to: Scalar[:str, :secure]
31
+ map :debug, :concurrency, :max_size, to: Scalar[:str, :int, :secure]
32
+ end
33
+
34
+ map :artifacts, to: Artifacts, drop_empty: false
35
+ map :code_climate, to: Addon[:repo_token], drop_empty: false
36
+ map :coverity_scan, to: CoverityScan
37
+ map :firefox, to: Version
38
+ map :hosts, to: Sequence
39
+ map :postgresql, to: Version
40
+ map :sauce_connect, to: Addon[:username, :access_key], drop_empty: false
41
+ map :ssh_known_hosts, to: Sequence
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,7 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Android < Mapping
4
+ map :components, :licenses, to: VersionList
5
+ end
6
+ end
7
+ end
@@ -1,14 +1,16 @@
1
1
  module Travis::Yaml
2
2
  module Nodes
3
3
  class Cache < Mapping
4
- map :apt, :bundler, to: Scalar[:bool]
4
+ map :apt, :bundler, :cocoapods, to: Scalar[:bool]
5
+ map :edge, to: Scalar[:bool], experimental: true
5
6
  map :directories, to: Sequence
6
7
 
7
8
  def visit_scalar(visitor, type, value, implicit = true)
8
9
  case type
9
10
  when :bool
10
- visit_key_value(visitor, :bundler, value)
11
- visit_key_value(visitor, :apt, value)
11
+ visit_key_value(visitor, :bundler, value)
12
+ visit_key_value(visitor, :apt, value)
13
+ visit_key_value(visitor, :cocoapods, value)
12
14
  when :str
13
15
  key = visitor.generate_key(self, value)
14
16
  self[key] = true
@@ -4,7 +4,8 @@ module Travis::Yaml
4
4
  include LanguageSpecific
5
5
  map :jdk, :node, :perl, :php, :python, :ruby, :scala, :node, to: Version
6
6
  map :rvm, to: :ruby
7
- map :repo, :branch, :condition, to: Scalar[:str]
7
+ map :repo, to: Scalar[:str]
8
+ map :branch, :condition, to: Sequence[:str]
8
9
  map :all_branches, :tags, to: Scalar[:bool]
9
10
  prefix_scalar :branch
10
11
  end
@@ -1,10 +1,56 @@
1
1
  module Travis::Yaml
2
2
  module Nodes
3
3
  class DeployEntry < OpenMapping
4
+ class Setting < OpenMapping
5
+ KEY = ''
6
+ prefix_scalar KEY
7
+ default_type Scalar[:str, :secure]
8
+
9
+ def ==(other)
10
+ return true if super
11
+ return false unless branch_specific?
12
+ generic == other
13
+ end
14
+
15
+ def __getobj__
16
+ branch_specific? ? generic : super
17
+ end
18
+
19
+ def inspect
20
+ branch_specific? ? generic.inspect : super
21
+ end
22
+
23
+ def branch_specific?
24
+ @mapping.size == 1 and @mapping.include? KEY
25
+ end
26
+
27
+ def branches
28
+ @mapping.keys - [KEY]
29
+ end
30
+
31
+ def generic
32
+ @mapping[KEY]
33
+ end
34
+
35
+ def verify_branch(name)
36
+ branches.each do |branch|
37
+ next if branch.to_s == name.to_s
38
+ warning "branch %p not permitted by deploy condition, dropping", branch
39
+ @mapping.delete(branch)
40
+ end
41
+ end
42
+ end
43
+
44
+ default_type Setting
4
45
  prefix_scalar :provider
5
46
  map :provider, to: Scalar, required: true
6
47
  map :edge, to: Scalar[:bool], experimental: true
7
48
  map :on, to: DeployConditions
49
+
50
+ def verify
51
+ @mapping.each_value { |v| v.verify_branch(on.branch) if v.respond_to? :verify_branch } if on and on.branch
52
+ super
53
+ end
8
54
  end
9
55
  end
10
56
  end
@@ -0,0 +1,6 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Dist < Scalar
4
+ end
5
+ end
6
+ end
@@ -11,7 +11,7 @@ module Travis::Yaml
11
11
  visitor.apply_mapping(self, value)
12
12
  end
13
13
 
14
- def visit_mapping(visitor, key, value)
14
+ def visit_key_value(visitor, key, value)
15
15
  key = visitor.generate_key(self, key)
16
16
  node = Scalar.new
17
17
  visitor.accept(node, value)
@@ -0,0 +1,6 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Group < Scalar
4
+ end
5
+ end
6
+ end
@@ -9,10 +9,11 @@ module Travis::Yaml
9
9
  value jvm: :java, javascript: :node_js, node: :node_js, nodejs: :node_js, golang: :go,
10
10
  objective_c: :"objective-c", obj_c: :"objective-c", objc: :"objective-c"
11
11
  value "c++" => :cpp, "node.js" => :node_js, "obj-c" => :"objective-c"
12
+ value :generic, bash: :generic, sh: :generic, shell: :generic
12
13
 
13
14
  def default_os
14
- value == "objective-c" ? :osx : OSEntry.default
15
+ value == "objective-c" ? "osx" : OSEntry.default
15
16
  end
16
17
  end
17
18
  end
18
- end
19
+ end
@@ -24,12 +24,12 @@ module Travis::Yaml
24
24
  gemfile: %w[ruby objective-c],
25
25
  composer_args: %w[php],
26
26
  npm_args: %w[node_js],
27
- sdk_components: %w[android]
27
+ android: %w[android]
28
28
  }
29
29
 
30
30
  def verify_language(language)
31
31
  LANGUAGE_SPECIFIC.each do |key, languages|
32
- next unless include? key and not languages.include? language
32
+ next unless include? key and not languages.include? language.value
33
33
  mapping.delete mapped_key(key)
34
34
  warning "specified %p, but setting is not relevant for %p", key.to_s, language
35
35
  end
@@ -42,17 +42,27 @@ module Travis::Yaml
42
42
  prefix_scalar(key)
43
43
  end
44
44
 
45
- def self.prefix_sequence(key)
46
- define_method(:visit_sequence) do |visitor, value|
47
- visit_key_value(visitor, key, value)
45
+ def self.prefix_sequence(key = nil)
46
+ @prefix_sequence ||= superclass.respond_to?(:prefix_sequence) ? superclass.prefix_sequence : nil
47
+ if key
48
+ @prefix_sequence = key.to_s
49
+ define_method(:visit_sequence) do |visitor, value|
50
+ visit_key_value(visitor, key, value)
51
+ end
48
52
  end
53
+ @prefix_sequence
49
54
  end
50
55
 
51
- def self.prefix_scalar(key, *types)
52
- define_method(:visit_scalar) do |visitor, type, value, implicit = true|
53
- return super(visitor, type, value, implicit = true) if types.any? and not types.include?(type)
54
- visit_key_value(visitor, key, value)
56
+ def self.prefix_scalar(key = nil, *types)
57
+ @prefix_scalar ||= superclass.respond_to?(:prefix_scalar) ? superclass.prefix_scalar : nil
58
+ if key
59
+ @prefix_scalar = key.to_s
60
+ define_method(:visit_scalar) do |visitor, type, value, implicit = true|
61
+ return super(visitor, type, value, implicit = true) if types.any? and not types.include?(type)
62
+ visit_key_value(visitor, key, value)
63
+ end
55
64
  end
65
+ @prefix_scalar
56
66
  end
57
67
 
58
68
  def self.define_map_accessor(key)
@@ -84,6 +94,7 @@ module Travis::Yaml
84
94
 
85
95
  def visit_key_value(visitor, key, value)
86
96
  return warning("unexpected key %p, dropping", key) unless node = subnode_for(key)
97
+ warning("has multiple %p entries, keeping last entry", key) if self[key]
87
98
  self[key] = node
88
99
  visitor.accept(node, value)
89
100
  end
@@ -93,12 +104,12 @@ module Travis::Yaml
93
104
  end
94
105
 
95
106
  def []=(key, value)
96
- if key = mapped_key(key)
107
+ if mapped_key = mapped_key(key)
97
108
  unless value.is_a? Node
98
- node = subnode_for(key)
109
+ node = subnode_for(mapped_key)
99
110
  value = node if Parser::Ruby.new(value).parse(node)
100
111
  end
101
- @mapping[key] = value
112
+ @mapping[mapped_key] = value
102
113
  else
103
114
  warning("unexpected key %p, dropping", key)
104
115
  end
@@ -199,6 +210,24 @@ module Travis::Yaml
199
210
  list = value.nested_warnings(*prefix, key) + list
200
211
  end
201
212
  end
213
+
214
+ def with_value!(value)
215
+ value = value.mapping while value.is_a? Mapping
216
+ value.each { |key, value| self[key] = value }
217
+ end
218
+
219
+ def each_scalar(type = nil, &block)
220
+ return enum_for(:each_scalar, type) unless block
221
+ @mapping.each_value { |v| v.each_scalar(type, &block) }
222
+ end
223
+
224
+ protected
225
+
226
+ def dup_values
227
+ duped_mapping = @mapping.map { |key, value| [key.dup, value.dup] }
228
+ @mapping = Hash[duped_mapping]
229
+ self
230
+ end
202
231
  end
203
232
  end
204
233
  end
@@ -5,7 +5,7 @@ module Travis::Yaml
5
5
  false
6
6
  end
7
7
 
8
- attr_accessor :partent
8
+ attr_accessor :parent
9
9
  def initialize(parent)
10
10
  @nested_warnings = []
11
11
  @parent = parent
@@ -97,6 +97,63 @@ module Travis::Yaml
97
97
  def to_s
98
98
  __getobj__.to_s
99
99
  end
100
+
101
+ def decrypt(&block)
102
+ each_scalar(SecureString) { |v| v.decrypt(&block) }
103
+ end
104
+
105
+ def encrypt(&block)
106
+ each_scalar(SecureString) { |v| v.encrypt(&block) }
107
+ end
108
+
109
+ def decrypted?
110
+ each_scalar(SecureString).all? { |v| v.decrypted? }
111
+ end
112
+
113
+ def encrypted?
114
+ each_scalar(SecureString).all? { |v| v.encrypted? }
115
+ end
116
+
117
+ def serialize(serializer, options = nil)
118
+ Serializer[serializer].serialize(self, options)
119
+ end
120
+
121
+ def to_yaml(options = nil)
122
+ serialize(:yaml, options)
123
+ end
124
+
125
+ def to_json(options = nil)
126
+ serialize(:json, options)
127
+ end
128
+
129
+ def to_ruby(options = nil)
130
+ serialize(:ruby, options)
131
+ end
132
+
133
+ def to_legacy_ruby(options = nil)
134
+ serialize(:legacy, options)
135
+ end
136
+
137
+ def with_value(value)
138
+ node = dup
139
+ node.with_value!(value)
140
+ node
141
+ end
142
+
143
+ def dup
144
+ super.dup_values
145
+ end
146
+
147
+ protected
148
+
149
+ def dup_values
150
+ self
151
+ end
152
+
153
+ def dup_ivar(name)
154
+ instance_variable_set(name, instance_variable_get(name).dup)
155
+ rescue TypeError
156
+ end
100
157
  end
101
158
  end
102
159
  end
@@ -3,6 +3,10 @@ module Travis::Yaml
3
3
  class Notifications < Mapping
4
4
  Callbacks ||= FixedValue[:always, :never, :change]
5
5
 
6
+ class List < Sequence
7
+ type Scalar[:str, :secure]
8
+ end
9
+
6
10
  class Notification < Mapping
7
11
  map :enabled, :disabled, to: Scalar[:bool]
8
12
  map :on_success, :on_failure, :on_start, to: Callbacks
@@ -12,7 +16,7 @@ module Travis::Yaml
12
16
  end
13
17
 
14
18
  def self.list(name)
15
- map name, to: Sequence
19
+ map name, to: List
16
20
  prefix_sequence name
17
21
  prefix_scalar name, :str, :secure
18
22
  end
@@ -32,7 +36,7 @@ module Travis::Yaml
32
36
  end
33
37
 
34
38
  class Template < Sequence
35
- VARIABLES = %w[repository_slug repository_name repository build_number branch commit author message duration compare_url build_url]
39
+ VARIABLES = %w[repository_slug repository_name repository build_number branch commit author message duration compare_url build_url commit_message]
36
40
 
37
41
  def verify
38
42
  super
@@ -60,18 +64,18 @@ module Travis::Yaml
60
64
  list :rooms
61
65
  end
62
66
 
63
- class Flowdoc < Notification
67
+ class Flowdock < Notification
64
68
  map :api_token, to: Scalar[:str, :secure]
65
- prefix_scalar name, :str, :secure
69
+ prefix_scalar :api_token, :str, :secure
66
70
  end
67
71
 
68
72
  map :webhooks, to: Notification[:urls]
69
73
  map :email, to: Notification[:recipients]
70
74
  map :sqwiggle, :slack, :campfire, to: WithTemplate[:rooms]
71
- map :flowdoc, to: Flowdoc
75
+ map :flowdock, to: Flowdock
72
76
  map :hipchat, to: Hipchat
73
77
  map :irc, to: IRC
74
78
  map :webhook, to: :webhooks
75
79
  end
76
80
  end
77
- end
81
+ end