travis-yaml 0.1.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.
- 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
|