travis-conditions 0.0.1
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/Gemfile +7 -0
- data/Gemfile.lock +28 -0
- data/README.md +46 -0
- data/lib/travis/conditions.rb +26 -0
- data/lib/travis/conditions/data.rb +45 -0
- data/lib/travis/conditions/eval.rb +68 -0
- data/lib/travis/conditions/parser.rb +202 -0
- data/lib/travis/conditions/version.rb +5 -0
- data/spec/data_spec.rb +20 -0
- data/spec/eval_spec.rb +148 -0
- data/spec/parser_spec.rb +275 -0
- data/spec/spec_helper.rb +1 -0
- metadata +68 -0
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
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
|
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
|
data/spec/parser_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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: []
|