travis-conditions 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c260f529b4432a50705b9296e7f5bff49fdb199a
4
+ data.tar.gz: 75623d8e139dcd3c7b34e152a72e80d7b49ffeb7
5
+ SHA512:
6
+ metadata.gz: 14a0df58f59d5d04c76830f506c4f080e3b50c6fa0b95d21612786a25c499f7fc64526792983b544b85bb5e260adf3ecaac40bc6a8a772dbe23e718db3c287f6
7
+ data.tar.gz: 1a4f2b466d2c85c858d018a88dd2bfdd02f973756d880ffa69d058a6724ddaf4606cd0fbed7e48846b31d48b7d437a97af040e7103bdc6718c64c88672940e60
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'parslet'
4
+
5
+ group :test do
6
+ gem 'rspec'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,28 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.3)
5
+ parslet (1.8.0)
6
+ rspec (3.6.0)
7
+ rspec-core (~> 3.6.0)
8
+ rspec-expectations (~> 3.6.0)
9
+ rspec-mocks (~> 3.6.0)
10
+ rspec-core (3.6.0)
11
+ rspec-support (~> 3.6.0)
12
+ rspec-expectations (3.6.0)
13
+ diff-lcs (>= 1.2.0, < 2.0)
14
+ rspec-support (~> 3.6.0)
15
+ rspec-mocks (3.6.0)
16
+ diff-lcs (>= 1.2.0, < 2.0)
17
+ rspec-support (~> 3.6.0)
18
+ rspec-support (3.6.0)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ parslet
25
+ rspec
26
+
27
+ BUNDLED WITH
28
+ 1.15.4
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # Boolean language for conditional builds, stages, jobs (draft)
2
+
3
+ ```
4
+ (NOT [term] OR [term]) AND [term]
5
+ ```
6
+
7
+ A term can be can be:
8
+
9
+ #### Equality
10
+
11
+ ```
12
+ branch = master
13
+ env(foo) = bar
14
+ ```
15
+
16
+ #### Match
17
+
18
+ ```
19
+ branch =~ ^master$
20
+ env(foo) =~ ^bar$
21
+ ```
22
+
23
+ #### Include
24
+
25
+ ```
26
+ branch IN (master, dev)
27
+ env(foo) IN (bar, baz)
28
+ ```
29
+
30
+ #### Presence
31
+
32
+ ```
33
+ branch IS present
34
+ branch IS blank
35
+ env(foo) IS present
36
+ env(foo) IS blank
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ ```ruby
42
+ str = 'branch IN (foo, bar) AND env(baz) =~ ^baz- OR tag IS present'
43
+ data = { branch: 'foo', env: { baz: 'baz-1' }, tag: 'v.1.0.0' }
44
+ Travis::Conditions.apply(str, data)
45
+ # => true
46
+ ```
@@ -0,0 +1,26 @@
1
+ require 'travis/conditions/data'
2
+ require 'travis/conditions/eval'
3
+ require 'travis/conditions/parser'
4
+
5
+ module Travis
6
+ module Conditions
7
+ class << self
8
+ def eval(str, data)
9
+ Eval.new(parse(str, keys: data.keys), Data.new(data)).apply
10
+ end
11
+
12
+ def parse(str, opts = {})
13
+ tree = parser(opts).parse(str)
14
+ Transform.new.apply(tree)
15
+ end
16
+
17
+ def parser(opts)
18
+ parsers[opts] ||= Parser.new(opts)
19
+ end
20
+
21
+ def parsers
22
+ @parsers ||= {}
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,45 @@
1
+ module Travis
2
+ module Conditions
3
+ class Data < Struct.new(:data)
4
+ def initialize(data)
5
+ data = symbolize(data)
6
+ data[:env] = symbolize(to_h(data[:env] || {}))
7
+ super(data)
8
+ end
9
+
10
+ def [](key)
11
+ data[key.to_sym]
12
+ end
13
+
14
+ def env(key)
15
+ data.fetch(:env, {})[key.to_sym]
16
+ end
17
+
18
+ private
19
+
20
+ def to_h(obj)
21
+ case obj
22
+ when Hash
23
+ obj
24
+ else
25
+ Array(obj).map { |value| split(value.to_s) }.to_h
26
+ end
27
+ end
28
+
29
+ def split(str)
30
+ str.split('=', 2)
31
+ end
32
+
33
+ def symbolize(value)
34
+ case value
35
+ when Hash
36
+ value.map { |key, value| [key.to_sym, symbolize(value)] }.to_h
37
+ when Array
38
+ value.map { |value| symbolize(value) }
39
+ else
40
+ value
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,68 @@
1
+ module Travis
2
+ module Conditions
3
+ class Eval < Struct.new(:sexp, :data)
4
+ def apply
5
+ !!evl(sexp)
6
+ end
7
+
8
+ private
9
+
10
+ def evl(value)
11
+ case value
12
+ when Array
13
+ send(*value)
14
+ else
15
+ data[value]
16
+ end
17
+ end
18
+
19
+ def not(lft)
20
+ !evl(lft)
21
+ end
22
+
23
+ def or(lft, rgt)
24
+ evl(lft) || evl(rgt)
25
+ end
26
+
27
+ def and(lft, rgt)
28
+ evl(lft) && evl(rgt)
29
+ end
30
+
31
+ def eq(lft, rgt)
32
+ evl(lft) == rgt
33
+ end
34
+
35
+ def not_eq(lft, rgt)
36
+ not eq(lft, rgt)
37
+ end
38
+
39
+ def match(lft, rgt)
40
+ evl(lft) =~ Regexp.new(rgt)
41
+ end
42
+
43
+ def not_match(lft, rgt)
44
+ not match(lft, rgt)
45
+ end
46
+
47
+ def in(lft, rgt)
48
+ rgt.include?(evl(lft))
49
+ end
50
+
51
+ def is(lft, rgt)
52
+ send(rgt, evl(lft))
53
+ end
54
+
55
+ def env(key)
56
+ data.env(key)
57
+ end
58
+
59
+ def present(value)
60
+ value.respond_to?(:empty?) && !value.empty?
61
+ end
62
+
63
+ def blank(value)
64
+ !present(value)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,202 @@
1
+ require 'parslet'
2
+
3
+ module Travis
4
+ module Conditions
5
+ class Parser < Struct.new(:opts)
6
+ FUNCS = %w(env)
7
+ PRESENCE = %w(present blank)
8
+
9
+ def parse(str)
10
+ parser.parse(str)
11
+ end
12
+
13
+ private
14
+
15
+ def parser
16
+ @parser ||= define_parser(opts[:keys]).new
17
+ end
18
+
19
+ def define_parser(keywords)
20
+ Class.new(Parslet::Parser) do
21
+ root :expr_or
22
+
23
+ rule :expr_or do
24
+ spaced(expr_and.as(:lft), op_or, expr_or.as(:rgt)).as(:or) | expr_and
25
+ end
26
+
27
+ rule :expr_and do
28
+ spaced(expr_inner.as(:lft), op_and, expr_and.as(:rgt)).as(:and) | expr_inner
29
+ end
30
+
31
+ rule :expr_inner do
32
+ lnot(parens(expr_or) | expr_incl | expr_is | expr_regex | expr_cmp)
33
+ end
34
+
35
+ rule :expr_cmp do
36
+ spaced(lhs.as(:lft), op_cmp.as(:op), value.as(:rgt)).as(:cmp)
37
+ end
38
+
39
+ rule :expr_regex do
40
+ spaced(lhs.as(:lft), op_regex.as(:op), regex.as(:rgt)).as(:cmp)
41
+ end
42
+
43
+ rule :expr_incl do
44
+ spaced(lhs.as(:lft), op_incl, parens(list).as(:rgt)).as(:incl)
45
+ end
46
+
47
+ rule :expr_is do
48
+ spaced(lhs.as(:lft), op_is, presence.as(:rgt)).as(:is)
49
+ end
50
+
51
+ def lnot(node)
52
+ (stri('not').maybe >> space >> node.as(:rgt)).as(:not) | node
53
+ end
54
+
55
+ rule :list do
56
+ (value >> (ts(str(',')) >> value).repeat)
57
+ end
58
+
59
+ rule :lhs do
60
+ func | keyword
61
+ end
62
+
63
+ rule :keyword do
64
+ stris(*keywords)
65
+ end
66
+
67
+ rule :func do
68
+ (stris(*FUNCS).as(:name) >> parens(word)).as(:func)
69
+ end
70
+
71
+ rule :word do
72
+ match('[\w_\-]').repeat(1).as(:str)
73
+ end
74
+
75
+ rule :regex do
76
+ quoted('/') | match('[\S]').repeat(1).as(:str)
77
+ end
78
+
79
+ rule :value do
80
+ unquoted | quoted('"') | quoted("'")
81
+ end
82
+
83
+ rule :unquoted do
84
+ match('[^\s\"\'\(\),]').repeat(1).as(:str)
85
+ end
86
+
87
+ def quoted(chr)
88
+ str(chr) >> match("[^#{chr}]").repeat.as(:str) >> str(chr)
89
+ end
90
+
91
+ rule :presence do
92
+ (stris(*PRESENCE)).as(:str)
93
+ end
94
+
95
+ rule :op_is do
96
+ stri('is')
97
+ end
98
+
99
+ rule :op_cmp do
100
+ str('=') | str('!=')
101
+ end
102
+
103
+ rule :op_regex do
104
+ str('=~') | str('!~')
105
+ end
106
+
107
+ rule :op_incl do
108
+ stri('in')
109
+ end
110
+
111
+ rule :op_or do
112
+ stri('or')
113
+ end
114
+
115
+ rule :op_and do
116
+ stri('and')
117
+ end
118
+
119
+ rule :space do
120
+ match('\s').repeat(1)
121
+ end
122
+
123
+ rule :space? do
124
+ space.maybe
125
+ end
126
+
127
+ def stris(*strs)
128
+ strs.inject(stri(strs.shift)) { |node, str| node | stri(str) }
129
+ end
130
+
131
+ def stri(str)
132
+ str(str) | str(str.upcase)
133
+ end
134
+
135
+ def parens?(node)
136
+ spaced?(str('(').maybe, node, str(')').maybe)
137
+ end
138
+
139
+ def parens(node)
140
+ spaced?(str('('), node, str(')'))
141
+ end
142
+
143
+ def spaced?(*nodes)
144
+ nodes.zip([space?] * nodes.size).flatten[0..-2].inject(&:>>)
145
+ end
146
+
147
+ def spaced(*nodes)
148
+ # nodes.zip([space] * nodes.size).flatten[0..-2].inject(&:>>)
149
+ nodes.zip([space?] * nodes.size).flatten[0..-2].inject(&:>>)
150
+ end
151
+
152
+ def ls(node)
153
+ space? >> node
154
+ end
155
+
156
+ def ts(node)
157
+ node >> space?
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ class Transform < Parslet::Transform
164
+ OP = {
165
+ '=' => :eq,
166
+ '!=' => :not_eq,
167
+ '=~' => :match,
168
+ '!~' => :not_match,
169
+ }
170
+
171
+ str = ->(node) { node.is_a?(Hash) ? node[:str].to_s : node.to_s }
172
+ sym = ->(node) { str.(node).downcase.to_sym }
173
+ func = ->(node) { [sym.(node[:func][:name]), str.(node[:func])] }
174
+ list = ->(node) { node.is_a?(Array) ? node.map { |v| str.(v) } : [str.(node)] }
175
+ lhs = ->(node) { node.is_a?(Hash) && node.key?(:func) ? func.(node) : str.(node) }
176
+
177
+ rule not: { rgt: subtree(:rgt) } do
178
+ [:not, rgt]
179
+ end
180
+
181
+ rule or: { lft: subtree(:lft), rgt: subtree(:rgt) } do
182
+ [:or, lft, rgt]
183
+ end
184
+
185
+ rule and: { lft: subtree(:lft), rgt: subtree(:rgt) } do
186
+ [:and, lft, rgt]
187
+ end
188
+
189
+ rule cmp: { op: simple(:op), lft: subtree(:lft), rgt: subtree(:rgt) } do
190
+ [OP[op.to_s], lhs.(lft), str.(rgt)]
191
+ end
192
+
193
+ rule incl: { lft: subtree(:lft), rgt: subtree(:rgt) } do
194
+ [:in, lhs.(lft), list.(rgt)]
195
+ end
196
+
197
+ rule is: { lft: subtree(:lft), rgt: subtree(:rgt) } do
198
+ [:is, lhs.(lft), sym.(rgt)]
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,5 @@
1
+ module Travis
2
+ module Conditions
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
data/spec/data_spec.rb ADDED
@@ -0,0 +1,20 @@
1
+ describe Travis::Conditions::Data do
2
+ let(:env) { nil }
3
+ let(:data) { { branch: 'branch', env: env } }
4
+ subject { described_class.new(data) }
5
+
6
+ it { expect(subject[:branch]).to eq 'branch' }
7
+ it { expect(subject['branch']).to eq 'branch' }
8
+
9
+ describe 'given an env hash' do
10
+ let(:env) { { foo: 'FOO' } }
11
+ it { expect(subject.env(:foo)).to eq 'FOO' }
12
+ it { expect(subject.env('foo')).to eq 'FOO' }
13
+ end
14
+
15
+ describe 'given an env array' do
16
+ let(:env) { ['foo=FOO'] }
17
+ it { expect(subject.env(:foo)).to eq 'FOO' }
18
+ it { expect(subject.env('foo')).to eq 'FOO' }
19
+ end
20
+ end
data/spec/eval_spec.rb ADDED
@@ -0,0 +1,148 @@
1
+ describe Travis::Conditions, 'eval' do
2
+ let(:tag) { nil }
3
+ let(:data) { { branch: 'master', tag: tag, env: { foo: 'foo' } } }
4
+ subject { described_class.eval(str, data) }
5
+
6
+ describe 'expressions' do
7
+ context do
8
+ let(:str) { 'NOT branch = foo AND (env(foo) = foo OR tag = wat)' }
9
+ it { should be true }
10
+ end
11
+
12
+ context do
13
+ let(:str) { 'branch = foo OR env(foo) = foo AND NOT tag = wat' }
14
+ it { should be true }
15
+ end
16
+
17
+ context do
18
+ let(:str) { 'branch = foo OR env(foo) = foo AND tag = wat' }
19
+ it { should be false }
20
+ end
21
+
22
+ context do
23
+ let(:tag) { '0.0.1' }
24
+ let(:str) { 'tag =~ /^(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?(?:-([\w.-]+))?(?:\+([\w.-]+))?$ AND type IN (push, api)/' }
25
+ it { should be false }
26
+ end
27
+ end
28
+
29
+ describe 'eq' do
30
+ context do
31
+ let(:str) { 'branch = master' }
32
+ it { should be true }
33
+ end
34
+
35
+ context do
36
+ let(:str) { 'branch = foo' }
37
+ it { should be false }
38
+ end
39
+
40
+ context do
41
+ let(:str) { 'env(foo) = foo' }
42
+ it { should be true }
43
+ end
44
+
45
+ context do
46
+ let(:str) { 'env(foo) = bar' }
47
+ it { should be false }
48
+ end
49
+ end
50
+
51
+ describe 'not eq' do
52
+ context do
53
+ let(:str) { 'branch != master' }
54
+ it { should be false }
55
+ end
56
+
57
+ context do
58
+ let(:str) { 'branch != foo' }
59
+ it { should be true }
60
+ end
61
+
62
+ context do
63
+ let(:str) { 'env(foo) != foo' }
64
+ it { should be false }
65
+ end
66
+
67
+ context do
68
+ let(:str) { 'env(foo) != bar' }
69
+ it { should be true }
70
+ end
71
+ end
72
+
73
+ describe 'match' do
74
+ context do
75
+ let(:str) { 'branch =~ ^ma.*$' }
76
+ it { should be true }
77
+ end
78
+
79
+ context do
80
+ let(:str) { 'branch =~ ^foo.*$' }
81
+ it { should be false }
82
+ end
83
+
84
+ context do
85
+ let(:str) { 'env(foo) =~ ^foo.*$' }
86
+ it { should be true }
87
+ end
88
+
89
+ context do
90
+ let(:str) { 'env(foo) =~ ^bar.*$' }
91
+ it { should be false }
92
+ end
93
+ end
94
+
95
+ describe 'in' do
96
+ context do
97
+ let(:str) { 'branch IN (foo, master, bar)' }
98
+ it { should be true }
99
+ end
100
+
101
+ context do
102
+ let(:str) { 'branch IN (foo, bar)' }
103
+ it { should be false }
104
+ end
105
+
106
+ context do
107
+ let(:str) { 'env(foo) IN (foo, bar, baz)' }
108
+ it { should be true }
109
+ end
110
+
111
+ context do
112
+ let(:str) { 'env(foo) IN (bar, baz)' }
113
+ it { should be false }
114
+ end
115
+ end
116
+
117
+ describe 'is' do
118
+ context do
119
+ let(:str) { 'branch IS present' }
120
+ it { should be true }
121
+ end
122
+
123
+ context do
124
+ let(:str) { 'tag IS present' }
125
+ it { should be false }
126
+ end
127
+
128
+ context do
129
+ let(:str) { 'branch IS blank' }
130
+ it { should be false }
131
+ end
132
+
133
+ context do
134
+ let(:str) { 'tag IS blank' }
135
+ it { should be true }
136
+ end
137
+
138
+ context do
139
+ let(:str) { 'env(foo) IS present' }
140
+ it { should be true }
141
+ end
142
+
143
+ context do
144
+ let(:str) { 'env(bar) IS blank' }
145
+ it { should be true }
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,275 @@
1
+ describe Travis::Conditions do
2
+ let(:keys) { [:foo] }
3
+ subject { described_class.parse(str, keys: keys) }
4
+
5
+ describe 'eq' do
6
+ context do
7
+ let(:str) { 'foo = bar' }
8
+ it { should eq [:eq, 'foo', 'bar'] }
9
+ end
10
+
11
+ context do
12
+ let(:str) { 'foo =bar' }
13
+ it { should eq [:eq, 'foo', 'bar'] }
14
+ end
15
+
16
+ context do
17
+ let(:str) { 'foo= bar' }
18
+ it { should eq [:eq, 'foo', 'bar'] }
19
+ end
20
+
21
+ context do
22
+ let(:str) { 'foo=bar' }
23
+ it { should eq [:eq, 'foo', 'bar'] }
24
+ end
25
+
26
+ context do
27
+ let(:str) { 'foo = "bar baz"' }
28
+ it { should eq [:eq, 'foo', 'bar baz'] }
29
+ end
30
+
31
+ context do
32
+ let(:str) { 'foo="bar baz"' }
33
+ it { should eq [:eq, 'foo', 'bar baz'] }
34
+ end
35
+
36
+ context do
37
+ let(:str) { 'env(foo) = "bar baz"' }
38
+ it { should eq [:eq, [:env, 'foo'], 'bar baz'] }
39
+ end
40
+
41
+ context do
42
+ let(:str) { 'foo = true' }
43
+ it { should eq [:eq, 'foo', 'true'] }
44
+ end
45
+ end
46
+
47
+ describe 'not eq' do
48
+ context do
49
+ let(:str) { 'foo != bar' }
50
+ it { should eq [:not_eq, 'foo', 'bar'] }
51
+ end
52
+
53
+ context do
54
+ let(:str) { 'foo !=bar' }
55
+ it { should eq [:not_eq, 'foo', 'bar'] }
56
+ end
57
+
58
+ context do
59
+ let(:str) { 'foo!= bar' }
60
+ it { should eq [:not_eq, 'foo', 'bar'] }
61
+ end
62
+
63
+ context do
64
+ let(:str) { 'foo!=bar' }
65
+ it { should eq [:not_eq, 'foo', 'bar'] }
66
+ end
67
+
68
+ context do
69
+ let(:str) { 'foo != "bar baz"' }
70
+ it { should eq [:not_eq, 'foo', 'bar baz'] }
71
+ end
72
+
73
+ context do
74
+ let(:str) { 'foo!="bar baz"' }
75
+ it { should eq [:not_eq, 'foo', 'bar baz'] }
76
+ end
77
+
78
+ context do
79
+ let(:str) { 'env(foo) != "bar baz"' }
80
+ it { should eq [:not_eq, [:env, 'foo'], 'bar baz'] }
81
+ end
82
+ end
83
+
84
+ describe 'match' do
85
+ context do
86
+ let(:str) { 'foo =~ ^v[0-9]$' }
87
+ it { should eq [:match, 'foo', '^v[0-9]$'] }
88
+ end
89
+
90
+ context do
91
+ let(:str) { 'env(foo) =~ ^v[0-9]$' }
92
+ it { should eq [:match, [:env, 'foo'], '^v[0-9]$'] }
93
+ end
94
+
95
+ context do
96
+ let(:str) { 'foo =~ /^v[0-9]$/' }
97
+ it { should eq [:match, 'foo', '^v[0-9]$'] }
98
+ end
99
+
100
+ context do
101
+ let(:str) { 'NOT (foo =~ /^v[0-9]$/)' }
102
+ it { should eq [:not, [:match, 'foo', '^v[0-9]$']] }
103
+ end
104
+ end
105
+
106
+ describe 'not match' do
107
+ context do
108
+ let(:str) { 'foo !~ ^v[0-9]$' }
109
+ it { should eq [:not_match, 'foo', '^v[0-9]$'] }
110
+ end
111
+
112
+ context do
113
+ let(:str) { 'env(foo) !~ ^v[0-9]$' }
114
+ it { should eq [:not_match, [:env, 'foo'], '^v[0-9]$'] }
115
+ end
116
+ end
117
+
118
+ describe 'in' do
119
+ context do
120
+ let(:str) { 'foo IN (bar)' }
121
+ it { should eq [:in, 'foo', ['bar']] }
122
+ end
123
+
124
+ context do
125
+ let(:str) { 'foo IN (bar, baz, buz)' }
126
+ it { should eq [:in, 'foo', ['bar', 'baz', 'buz']] }
127
+ end
128
+
129
+ context do
130
+ let(:str) { 'foo IN (bar, "baz, buz")' }
131
+ it { should eq [:in, 'foo', ['bar', 'baz, buz']] }
132
+ end
133
+
134
+ context do
135
+ let(:str) { 'env(foo) IN (bar, "baz, buz")' }
136
+ it { should eq [:in, [:env, 'foo'], ['bar', 'baz, buz']] }
137
+ end
138
+ end
139
+
140
+ describe 'is' do
141
+ context do
142
+ let(:str) { 'foo IS present' }
143
+ it { should eq [:is, 'foo', :present] }
144
+ end
145
+
146
+ context do
147
+ let(:str) { 'foo IS PRESENT' }
148
+ it { should eq [:is, 'foo', :present] }
149
+ end
150
+
151
+ context do
152
+ let(:str) { 'foo IS blank' }
153
+ it { should eq [:is, 'foo', :blank] }
154
+ end
155
+
156
+ context do
157
+ let(:str) { 'foo IS BLANK' }
158
+ it { should eq [:is, 'foo', :blank] }
159
+ end
160
+
161
+ context do
162
+ let(:str) { 'env(foo) IS BLANK' }
163
+ it { should eq [:is, [:env, 'foo'], :blank] }
164
+ end
165
+ end
166
+
167
+ describe 'boolean' do
168
+ let(:keys) { [:a, :b, :c, :d] }
169
+
170
+ context do
171
+ let(:str) { 'a=1 OR b=2 AND c=3 OR d=4' }
172
+ it { should eq [:or, [:eq, 'a', '1'], [:or, [:and, [:eq, 'b', '2'], [:eq, 'c', '3']], [:eq, 'd', '4']]] }
173
+ end
174
+
175
+ context do
176
+ let(:str) { '(a=1) OR (b=2) AND (c=3) OR (d=4)' }
177
+ it { should eq [:or, [:eq, 'a', '1'], [:or, [:and, [:eq, 'b', '2'], [:eq, 'c', '3']], [:eq, 'd', '4']]] }
178
+ end
179
+
180
+ context do
181
+ let(:str) { '(a=1 OR b=2) AND (c=3 OR d=4)' }
182
+ it { should eq [:and, [:or, [:eq, 'a', '1'], [:eq, 'b', '2']], [:or, [:eq, 'c', '3'], [:eq, 'd', '4']]] }
183
+ end
184
+
185
+ context do
186
+ let(:str) { 'env(a)=1 OR env(b)=2 AND c=3' }
187
+ it { should eq [:or, [:eq, [:env, 'a'], '1'], [:and, [:eq, [:env, 'b'], '2'], [:eq, 'c', '3']]] }
188
+ end
189
+
190
+ context do
191
+ let(:str) { '(env(a)=1) OR (env(b)=2) AND (c=3)' }
192
+ it { should eq [:or, [:eq, [:env, 'a'], '1'], [:and, [:eq, [:env, 'b'], '2'], [:eq, 'c', '3']]] }
193
+ end
194
+
195
+ context do
196
+ let(:str) { '(env(a)=1 OR env(b)=2) AND c=3' }
197
+ it { should eq [:and, [:or, [:eq, [:env, 'a'], '1'], [:eq, [:env, 'b'], '2']], [:eq, 'c', '3']] }
198
+ end
199
+
200
+ context do
201
+ let(:str) { 'a IN (1) OR b IN (2) AND c IN (3)' }
202
+ it { should eq [:or, [:in, 'a', ['1']], [:and, [:in, 'b', ['2']], [:in, 'c', ['3']]]] }
203
+ end
204
+
205
+ context do
206
+ let(:str) { '(a IN (1) OR b IN (2)) AND (c IN (3))' }
207
+ it { should eq [:and, [:or, [:in, 'a', ['1']], [:in, 'b', ['2']]], [:in, 'c', ['3']]] }
208
+ end
209
+
210
+ context do
211
+ let(:str) { 'a IS present OR b IS blank AND c IS present' }
212
+ it { should eq [:or, [:is, 'a', :present], [:and, [:is, 'b', :blank], [:is, 'c', :present]]] }
213
+ end
214
+
215
+ context do
216
+ let(:str) { '(a IS present OR (b IS blank)) AND (c IS present)' }
217
+ it { should eq [:and, [:or, [:is, 'a', :present], [:is, 'b', :blank]], [:is, 'c', :present]] }
218
+ end
219
+
220
+ context do
221
+ let(:str) { 'NOT a=1' }
222
+ it { should eq [:not, [:eq, 'a', '1']] }
223
+ end
224
+
225
+ context do
226
+ let(:str) { 'NOT (a=1)' }
227
+ it { should eq [:not, [:eq, 'a', '1']] }
228
+ end
229
+
230
+ context do
231
+ let(:str) { 'NOT a=1 OR b=2' }
232
+ it { should eq [:or, [:not, [:eq, 'a', '1']], [:eq, 'b', '2']] }
233
+ end
234
+
235
+ context do
236
+ let(:str) { 'NOT (a=1 OR b=2)' }
237
+ it { should eq [:not, [:or, [:eq, 'a', '1'], [:eq, 'b', '2']]] }
238
+ end
239
+
240
+ context do
241
+ let(:str) { 'NOT a=1 OR NOT b=2' }
242
+ it { should eq [:or, [:not, [:eq, 'a', '1']], [:not, [:eq, 'b', '2']]] }
243
+ end
244
+
245
+ context do
246
+ let(:str) { 'NOT a=1 AND b=2' }
247
+ it { should eq [:and, [:not, [:eq, 'a', '1']], [:eq, 'b', '2']] }
248
+ end
249
+
250
+ context do
251
+ let(:str) { 'NOT (a=1 OR b=2)' }
252
+ it { should eq [:not, [:or, [:eq, 'a', '1'], [:eq, 'b', '2']]] }
253
+ end
254
+
255
+ context do
256
+ let(:str) { 'NOT (NOT (a=1) OR b=2) AND NOT (c=3) OR d=4' }
257
+ it { should eq [:or, [:and, [:not, [:or, [:not, [:eq, 'a', '1']], [:eq, 'b', '2']]], [:not, [:eq, 'c', '3']]], [:eq, 'd', '4']] }
258
+ end
259
+
260
+ context do
261
+ let(:str) { 'NOT env(a)=1' }
262
+ it { should eq [:not, [:eq, [:env, 'a'], '1']] }
263
+ end
264
+
265
+ context do
266
+ let(:str) { 'NOT (env(a)=1)' }
267
+ it { should eq [:not, [:eq, [:env, 'a'], '1']] }
268
+ end
269
+
270
+ context do
271
+ let(:str) { 'NOT (env(a)=1 OR env(b)=2)' }
272
+ it { should eq [:not, [:or, [:eq, [:env, 'a'], '1'],[:eq, [:env, 'b'], '2']]] }
273
+ end
274
+ end
275
+ end
@@ -0,0 +1 @@
1
+ require 'travis/conditions'
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: travis-conditions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Travis CI
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-10-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parslet
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email: contact@travis-ci.org
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - Gemfile
34
+ - Gemfile.lock
35
+ - README.md
36
+ - lib/travis/conditions.rb
37
+ - lib/travis/conditions/data.rb
38
+ - lib/travis/conditions/eval.rb
39
+ - lib/travis/conditions/parser.rb
40
+ - lib/travis/conditions/version.rb
41
+ - spec/data_spec.rb
42
+ - spec/eval_spec.rb
43
+ - spec/parser_spec.rb
44
+ - spec/spec_helper.rb
45
+ homepage: https://github.com/travis-ci/travis-conditions
46
+ licenses: []
47
+ metadata: {}
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project:
64
+ rubygems_version: 2.6.13
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Boolean language for conditional builds, stages, jobs
68
+ test_files: []