travis-yaml 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +3 -0
- data/.travis.yml +14 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +73 -0
- data/LICENSE +22 -0
- data/README.md +232 -0
- data/Rakefile +3 -0
- data/SPEC.md +1018 -0
- data/bench/parser_bench.rb +54 -0
- data/config.ru +2 -0
- data/lib/travis/yaml.rb +43 -0
- data/lib/travis/yaml/matrix.rb +65 -0
- data/lib/travis/yaml/nodes.rb +40 -0
- data/lib/travis/yaml/nodes/branches.rb +12 -0
- data/lib/travis/yaml/nodes/bundler_args.rb +6 -0
- data/lib/travis/yaml/nodes/cache.rb +29 -0
- data/lib/travis/yaml/nodes/compiler.rb +7 -0
- data/lib/travis/yaml/nodes/compiler_entry.rb +9 -0
- data/lib/travis/yaml/nodes/deploy.rb +7 -0
- data/lib/travis/yaml/nodes/deploy_conditions.rb +12 -0
- data/lib/travis/yaml/nodes/deploy_entry.rb +10 -0
- data/lib/travis/yaml/nodes/env.rb +36 -0
- data/lib/travis/yaml/nodes/fixed_value.rb +60 -0
- data/lib/travis/yaml/nodes/git.rb +9 -0
- data/lib/travis/yaml/nodes/jdk.rb +11 -0
- data/lib/travis/yaml/nodes/language.rb +18 -0
- data/lib/travis/yaml/nodes/language_specific.rb +45 -0
- data/lib/travis/yaml/nodes/mapping.rb +204 -0
- data/lib/travis/yaml/nodes/matrix.rb +36 -0
- data/lib/travis/yaml/nodes/node.rb +102 -0
- data/lib/travis/yaml/nodes/notifications.rb +77 -0
- data/lib/travis/yaml/nodes/open_mapping.rb +18 -0
- data/lib/travis/yaml/nodes/os.rb +21 -0
- data/lib/travis/yaml/nodes/os_entry.rb +19 -0
- data/lib/travis/yaml/nodes/root.rb +44 -0
- data/lib/travis/yaml/nodes/ruby.rb +11 -0
- data/lib/travis/yaml/nodes/scalar.rb +97 -0
- data/lib/travis/yaml/nodes/sequence.rb +84 -0
- data/lib/travis/yaml/nodes/stage.rb +6 -0
- data/lib/travis/yaml/nodes/version.rb +6 -0
- data/lib/travis/yaml/nodes/version_list.rb +7 -0
- data/lib/travis/yaml/nodes/virtual_env.rb +7 -0
- data/lib/travis/yaml/parser.rb +31 -0
- data/lib/travis/yaml/parser/dummy.rb +13 -0
- data/lib/travis/yaml/parser/psych.rb +217 -0
- data/lib/travis/yaml/parser/ruby.rb +77 -0
- data/lib/travis/yaml/secure_string.rb +12 -0
- data/lib/travis/yaml/version.rb +5 -0
- data/play/lint.rb +8 -0
- data/play/spec.rb +183 -0
- data/play/weblint.rb +296 -0
- data/spec/nodes/.rb +0 -0
- data/spec/nodes/branches_spec.rb +45 -0
- data/spec/nodes/bundler_args_spec.rb +9 -0
- data/spec/nodes/cache_spec.rb +55 -0
- data/spec/nodes/compiler_spec.rb +14 -0
- data/spec/nodes/deploy_spec.rb +83 -0
- data/spec/nodes/git_spec.rb +55 -0
- data/spec/nodes/jdk_spec.rb +41 -0
- data/spec/nodes/language_spec.rb +79 -0
- data/spec/nodes/notifications_spec.rb +45 -0
- data/spec/nodes/os_spec.rb +28 -0
- data/spec/nodes/ruby_spec.rb +69 -0
- data/spec/nodes/stage_spec.rb +34 -0
- data/spec/nodes/virtual_env_spec.rb +9 -0
- data/spec/parser/dummy_spec.rb +7 -0
- data/spec/parser/ruby_spec.rb +41 -0
- data/spec/support.rb +3 -0
- data/spec/support/coverage.rb +11 -0
- data/spec/support/environment.rb +1 -0
- data/spec/yaml_spec.rb +26 -0
- data/travis-yaml.gemspec +24 -0
- metadata +207 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module Travis::Yaml
|
2
|
+
module Nodes
|
3
|
+
module LanguageSpecific
|
4
|
+
LANGUAGE_SPECIFIC = {
|
5
|
+
bundler_args: %w[ruby],
|
6
|
+
compiler: %w[c cpp],
|
7
|
+
lein: %w[clojure],
|
8
|
+
otp_release: %w[erlang],
|
9
|
+
gobuild_args: %w[go],
|
10
|
+
go: %w[go],
|
11
|
+
jdk: %w[clojure groovy java ruby scala android],
|
12
|
+
ghc: %w[haskell],
|
13
|
+
node_js: %w[node_js],
|
14
|
+
ruby: %w[ruby objective-c],
|
15
|
+
xcode_sdk: %w[objective-c],
|
16
|
+
xcode_scheme: %w[objective-c],
|
17
|
+
xcode_project: %w[objective-c],
|
18
|
+
xcode_workspace: %w[objective-c],
|
19
|
+
xctool_args: %w[objective-c],
|
20
|
+
perl: %w[perl],
|
21
|
+
php: %w[php],
|
22
|
+
python: %w[python],
|
23
|
+
virtualenv: %w[python],
|
24
|
+
gemfile: %w[ruby objective-c],
|
25
|
+
composer_args: %w[php],
|
26
|
+
npm_args: %w[node_js],
|
27
|
+
sdk_components: %w[android]
|
28
|
+
}
|
29
|
+
|
30
|
+
def verify_language(language)
|
31
|
+
LANGUAGE_SPECIFIC.each do |key, languages|
|
32
|
+
next unless include? key and not languages.include? language
|
33
|
+
mapping.delete mapped_key(key)
|
34
|
+
warning "specified %p, but setting is not relevant for %p", key.to_s, language
|
35
|
+
end
|
36
|
+
|
37
|
+
mapping.each_value do |value|
|
38
|
+
value.verify_language(language) if value.respond_to? :verify_language
|
39
|
+
end
|
40
|
+
|
41
|
+
verify_errors
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
module Travis::Yaml
|
2
|
+
module Nodes
|
3
|
+
class Mapping < Node
|
4
|
+
def self.mapping
|
5
|
+
@mapping ||= superclass.respond_to?(:mapping) ? superclass.mapping.dup : {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.required
|
9
|
+
@required ||= superclass.respond_to?(:required) ? superclass.required.dup : []
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.experimental
|
13
|
+
@experimental ||= superclass.respond_to?(:experimental) ? superclass.experimental.dup : []
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.drop_empty
|
17
|
+
@drop_empty ||= superclass.respond_to?(:drop_empty) ? superclass.drop_empty.dup : []
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.aliases
|
21
|
+
@aliases ||= superclass.respond_to?(:aliases) ? superclass.aliases.dup : {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.map(*list)
|
25
|
+
options = Hash === list.last ? list.pop : {}
|
26
|
+
list.each do |key|
|
27
|
+
drop_empty << key.to_s if options.fetch(:drop_empty, true)
|
28
|
+
required << key.to_s if options[:required]
|
29
|
+
experimental << key.to_s if options[:experimental]
|
30
|
+
define_map_accessor(key)
|
31
|
+
case options[:to]
|
32
|
+
when Symbol then aliases[key.to_s] = options[:to].to_s
|
33
|
+
when Module then mapping[key.to_s] = options[:to]
|
34
|
+
when nil then mapping[key.to_s] = Nodes[key]
|
35
|
+
else raise ArgumentError, 'unexpected value for to: %p' % options[:to]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.auto_prefix(key)
|
41
|
+
prefix_sequence(key)
|
42
|
+
prefix_scalar(key)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.prefix_sequence(key)
|
46
|
+
define_method(:visit_sequence) do |visitor, value|
|
47
|
+
visit_key_value(visitor, key, value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
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)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.define_map_accessor(key)
|
59
|
+
define_method(key) { | | self[key] } unless method_defined? key
|
60
|
+
define_method("#{key}=") { |v| self[key] = v } unless method_defined? "#{key}="
|
61
|
+
define_method("#{key}?") { | | !!self[key] } unless method_defined? "#{key}?"
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.subnode_for(key)
|
65
|
+
mapping[aliases.fetch(key.to_s, key.to_s)]
|
66
|
+
end
|
67
|
+
|
68
|
+
attr_reader :mapping
|
69
|
+
alias_method :__getobj__, :mapping
|
70
|
+
|
71
|
+
def prepare
|
72
|
+
@mapping = {}
|
73
|
+
super
|
74
|
+
end
|
75
|
+
|
76
|
+
def visit_mapping(visitor, value)
|
77
|
+
visitor.apply_mapping(self, value)
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_pair(visitor, key, value)
|
81
|
+
key = visitor.generate_key(self, key)
|
82
|
+
visit_key_value(visitor, key, value)
|
83
|
+
end
|
84
|
+
|
85
|
+
def visit_key_value(visitor, key, value)
|
86
|
+
return warning("unexpected key %p, dropping", key) unless node = subnode_for(key)
|
87
|
+
self[key] = node
|
88
|
+
visitor.accept(node, value)
|
89
|
+
end
|
90
|
+
|
91
|
+
def drop_empty?(key)
|
92
|
+
self.class.drop_empty.include? mapped_key(key)
|
93
|
+
end
|
94
|
+
|
95
|
+
def []=(key, value)
|
96
|
+
if key = mapped_key(key)
|
97
|
+
unless value.is_a? Node
|
98
|
+
node = subnode_for(key)
|
99
|
+
value = node if Parser::Ruby.new(value).parse(node)
|
100
|
+
end
|
101
|
+
@mapping[key] = value
|
102
|
+
else
|
103
|
+
warning("unexpected key %p, dropping", key)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def [](key)
|
108
|
+
@mapping[mapped_key(key)]
|
109
|
+
end
|
110
|
+
|
111
|
+
def include?(key)
|
112
|
+
@mapping.include? mapped_key(key)
|
113
|
+
end
|
114
|
+
|
115
|
+
def empty?
|
116
|
+
@mapping.empty?
|
117
|
+
end
|
118
|
+
|
119
|
+
def mapped_key(key)
|
120
|
+
key = self.class.aliases.fetch(key.to_s, key.to_s)
|
121
|
+
key if accept_key?(key)
|
122
|
+
end
|
123
|
+
|
124
|
+
def accept_key?(key)
|
125
|
+
self.class.mapping.include? key
|
126
|
+
end
|
127
|
+
|
128
|
+
def subnode_for(key)
|
129
|
+
type = self.class.subnode_for(key)
|
130
|
+
type.new(self) if type
|
131
|
+
end
|
132
|
+
|
133
|
+
def inspect
|
134
|
+
@mapping.inspect
|
135
|
+
end
|
136
|
+
|
137
|
+
def ==(other)
|
138
|
+
other = other.mapping if other.is_a? Mapping
|
139
|
+
if other.respond_to? :to_hash and other.to_hash.size == @mapping.size
|
140
|
+
other.to_hash.all? { |k, v| include?(k) and self[k] == v }
|
141
|
+
else
|
142
|
+
false
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def verify
|
147
|
+
verify_experimental
|
148
|
+
verify_errors
|
149
|
+
verify_empty
|
150
|
+
verify_required
|
151
|
+
verify_errors
|
152
|
+
end
|
153
|
+
|
154
|
+
def verify_experimental
|
155
|
+
self.class.experimental.each do |key|
|
156
|
+
next unless @mapping.include? key
|
157
|
+
warning "%p is experimental and might be removed in the future", key
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def verify_empty
|
162
|
+
@mapping.delete_if do |key, value|
|
163
|
+
next unless drop_empty? key and value.empty?
|
164
|
+
value.nested_warnings.each { |p, w| nested_warning(w, key, *p) }
|
165
|
+
warning('value for %p section is empty, dropping', key)
|
166
|
+
true
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def verify_required
|
171
|
+
self.class.required.each do |key|
|
172
|
+
next if @mapping.include? key
|
173
|
+
type = self.class.subnode_for(key)
|
174
|
+
if type.has_default?
|
175
|
+
warning "missing key %p, defaulting to %p", key, type.default
|
176
|
+
@mapping[key] = type.new(self)
|
177
|
+
else
|
178
|
+
error "missing key %p", key
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def verify_errors
|
184
|
+
@mapping.delete_if do |key, value|
|
185
|
+
if value.errors?
|
186
|
+
warning "dropping %p section: %s", key, value.errors.join(', ')
|
187
|
+
true
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def deep_verify
|
193
|
+
@mapping.each_value(&:deep_verify)
|
194
|
+
super
|
195
|
+
end
|
196
|
+
|
197
|
+
def nested_warnings(*prefix)
|
198
|
+
@mapping.inject(super) do |list, (key, value)|
|
199
|
+
list = value.nested_warnings(*prefix, key) + list
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Travis::Yaml
|
2
|
+
module Nodes
|
3
|
+
class Matrix < Mapping
|
4
|
+
class Matcher < Mapping
|
5
|
+
include LanguageSpecific
|
6
|
+
|
7
|
+
map :ruby, :jdk, :lein, :otp_release, :go, :ghc, :node_js, :xcode_sdk,
|
8
|
+
:xcode_scheme, :perl, :php, :python, :gemfile, to: Version
|
9
|
+
map :rvm, to: :ruby
|
10
|
+
map :otp, to: :otp_release
|
11
|
+
map :node, to: :node_js
|
12
|
+
map :env, to: Env::Variables
|
13
|
+
map :os, to: OSEntry
|
14
|
+
map :compiler, to: CompilerEntry
|
15
|
+
end
|
16
|
+
|
17
|
+
class MatcherList < Sequence
|
18
|
+
type Matcher
|
19
|
+
|
20
|
+
def verify_language(language)
|
21
|
+
children.each { |c| c.verify_language(language) }
|
22
|
+
verify_children
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
map :include, :exclude, :allow_failures, to: MatcherList
|
27
|
+
map :fast_finish, to: Scalar[:bool]
|
28
|
+
|
29
|
+
def verify_language(language)
|
30
|
+
values.each { |v| v.verify_language(language) }
|
31
|
+
verify_empty
|
32
|
+
verify_errors
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Travis::Yaml
|
2
|
+
module Nodes
|
3
|
+
class Node
|
4
|
+
def self.has_default?
|
5
|
+
false
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor :partent
|
9
|
+
def initialize(parent)
|
10
|
+
@nested_warnings = []
|
11
|
+
@parent = parent
|
12
|
+
prepare
|
13
|
+
yield self if block_given?
|
14
|
+
end
|
15
|
+
|
16
|
+
def warngings?
|
17
|
+
warnings.any?
|
18
|
+
end
|
19
|
+
|
20
|
+
def errors?
|
21
|
+
errors.any?
|
22
|
+
end
|
23
|
+
|
24
|
+
def warnings
|
25
|
+
@warnings ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def nested_warning(message, *prefix)
|
29
|
+
@nested_warnings << [prefix, message]
|
30
|
+
end
|
31
|
+
|
32
|
+
def nested_warnings(*prefix)
|
33
|
+
messages = errors + warnings
|
34
|
+
prefixed = messages.map { |warning| [prefix, warning] }
|
35
|
+
prefixed += @nested_warnings.map { |p, w| [prefix + p, w] }
|
36
|
+
prefixed
|
37
|
+
end
|
38
|
+
|
39
|
+
def errors
|
40
|
+
@errors ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
def warning(message, *params)
|
44
|
+
warnings << message % params
|
45
|
+
end
|
46
|
+
|
47
|
+
def error(message, *params)
|
48
|
+
errors << message % params
|
49
|
+
end
|
50
|
+
|
51
|
+
def prepare
|
52
|
+
end
|
53
|
+
|
54
|
+
def verify
|
55
|
+
end
|
56
|
+
|
57
|
+
def verify_language(language)
|
58
|
+
end
|
59
|
+
|
60
|
+
def deep_verify
|
61
|
+
verify
|
62
|
+
end
|
63
|
+
|
64
|
+
def visit_unexpected(visitor, value, message = nil)
|
65
|
+
error(message || "unexpected %p", value)
|
66
|
+
end
|
67
|
+
|
68
|
+
def visit_mapping(visitor, value)
|
69
|
+
error("unexpected mapping")
|
70
|
+
end
|
71
|
+
|
72
|
+
def visit_pair(visitor, key, value)
|
73
|
+
error("unexpected pair")
|
74
|
+
end
|
75
|
+
|
76
|
+
def visit_scalar(visitor, type, value, implicit = true)
|
77
|
+
error("unexpected scalar") unless type == :null
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_sequence(visitor, value)
|
81
|
+
error("unexpected sequence")
|
82
|
+
end
|
83
|
+
|
84
|
+
def visit_child(visitor, value)
|
85
|
+
error("unexpected child")
|
86
|
+
end
|
87
|
+
|
88
|
+
def respond_to_missing?(method, include_private = false)
|
89
|
+
__getobj__.respond_to?(method, false)
|
90
|
+
end
|
91
|
+
|
92
|
+
def method_missing(method, *args, &block)
|
93
|
+
return super unless __getobj__.respond_to?(method)
|
94
|
+
__getobj__.public_send(method, *args, &block)
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
__getobj__.to_s
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Travis::Yaml
|
2
|
+
module Nodes
|
3
|
+
class Notifications < Mapping
|
4
|
+
Callbacks ||= FixedValue[:always, :never, :change]
|
5
|
+
|
6
|
+
class Notification < Mapping
|
7
|
+
map :enabled, :disabled, to: Scalar[:bool]
|
8
|
+
map :on_success, :on_failure, :on_start, to: Callbacks
|
9
|
+
|
10
|
+
def self.[](name)
|
11
|
+
Class.new(self) { list(name) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.list(name)
|
15
|
+
map name, to: Sequence
|
16
|
+
prefix_sequence name
|
17
|
+
prefix_scalar name, :str, :secure
|
18
|
+
end
|
19
|
+
|
20
|
+
def visit_scalar(visitor, type, value, implicit = true)
|
21
|
+
return super unless type == :bool
|
22
|
+
visit_key_value(visitor, :enabled, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def enabled?
|
26
|
+
@mapping.fetch('enabled', !@mapping['disabled'])
|
27
|
+
end
|
28
|
+
|
29
|
+
def disabled?
|
30
|
+
!enabled?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Template < Sequence
|
35
|
+
VARIABLES = %w[repository_slug repository_name repository build_number branch commit author message duration compare_url build_url]
|
36
|
+
|
37
|
+
def verify
|
38
|
+
super
|
39
|
+
@children.each do |child|
|
40
|
+
child.to_s.scan(/%{([^}]+)}/) do |match|
|
41
|
+
next if VARIABLES.include? match.first
|
42
|
+
warning 'unknown "template" variable %p', match.first
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class WithTemplate < Notification
|
49
|
+
map :template, to: Template
|
50
|
+
end
|
51
|
+
|
52
|
+
class IRC < WithTemplate
|
53
|
+
list :channels
|
54
|
+
map :channel_key, :password, :nickserv_password, :nick, to: Scalar[:str, :secure]
|
55
|
+
map :use_notice, :skip_join, to: Scalar[:bool]
|
56
|
+
end
|
57
|
+
|
58
|
+
class Hipchat < WithTemplate
|
59
|
+
map :format, to: FixedValue[:html, :text]
|
60
|
+
list :rooms
|
61
|
+
end
|
62
|
+
|
63
|
+
class Flowdoc < Notification
|
64
|
+
map :api_token, to: Scalar[:str, :secure]
|
65
|
+
prefix_scalar name, :str, :secure
|
66
|
+
end
|
67
|
+
|
68
|
+
map :webhooks, to: Notification[:urls]
|
69
|
+
map :email, to: Notification[:recipients]
|
70
|
+
map :sqwiggle, :slack, :campfire, to: WithTemplate[:rooms]
|
71
|
+
map :flowdoc, to: Flowdoc
|
72
|
+
map :hipchat, to: Hipchat
|
73
|
+
map :irc, to: IRC
|
74
|
+
map :webhook, to: :webhooks
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|