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
@@ -0,0 +1,32 @@
1
+ module Travis::Yaml
2
+ module Serializer
3
+ class Legacy < Ruby
4
+ def serialize_root(node)
5
+ return serialize_error(node) if node.errors?
6
+ super.merge(serialize_warnings(node))
7
+ end
8
+
9
+ def serialize_error(node)
10
+ {
11
+ serialize_key('.result') => 'parse_error',
12
+ serialize_key('.result_message') => node.errors.join("\n")
13
+ }
14
+ end
15
+
16
+ def serialize_warnings(node)
17
+ {
18
+ serialize_key('.result') => 'configured',
19
+ serialize_key('.result_warnings') => node.nested_warnings
20
+ }
21
+ end
22
+
23
+ def serialize_encrypted(value)
24
+ { serialize_key('secure') => value.encrypted_string }
25
+ end
26
+
27
+ def serialize_decrypted(value)
28
+ value.decrypted_string
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ module Travis::Yaml
2
+ module Serializer
3
+ class Ruby < Generic
4
+ def serialize_value(value)
5
+ value
6
+ end
7
+
8
+ def serialize_mapping(node)
9
+ Hash[super]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ module Travis::Yaml
2
+ module Serializer
3
+ class Yaml < Ruby
4
+ PSYCH_OPTIONS = [ :indentation, :line_width, :canonical ]
5
+
6
+ class Tagged
7
+ attr_accessor :tag, :value
8
+ def initialize(tag, value)
9
+ @tag, @value = tag, value
10
+ end
11
+
12
+ def encode_with(coder)
13
+ coder.represent_scalar(tag, value)
14
+ end
15
+ end
16
+
17
+ def self.serialize(node, options = nil)
18
+ psych_options = options.select { |key, _| PSYCH_OPTIONS.include? key } if options
19
+ ::Psych.dump(super, psych_options || {})
20
+ end
21
+
22
+ def avoid_tags?
23
+ !!options[:avoid_tags]
24
+ end
25
+
26
+ def serialize_encrypted(value)
27
+ value = value.encrypted_string
28
+ avoid_tags? ? { "secure" => value } : Tagged.new('!encrypted', value)
29
+ end
30
+
31
+ def serialize_decrypted(value)
32
+ value = value.decrypted_string
33
+ avoid_tags? ? value : Tagged.new('!decrypted', value)
34
+ end
35
+
36
+ def serialize_binary(value)
37
+ Tagged.new('!binary', [value].pack('m0'))
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  module Travis
2
2
  module Yaml
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.0'
4
4
  end
5
5
  end
@@ -34,7 +34,7 @@ module Travis::Yaml
34
34
  description
35
35
  end
36
36
 
37
- def self.spec_format
37
+ def self.spec_format(*)
38
38
  end
39
39
 
40
40
  def self.spec(*prefix, **options)
@@ -45,7 +45,7 @@ module Travis::Yaml
45
45
  end
46
46
 
47
47
  class Scalar
48
- def self.spec_format(append = "")
48
+ def self.spec_format(append = "", *)
49
49
  formats = cast.any? ? cast : [default_type]
50
50
  formats.map { |f| TYPES[f] ? TYPES[f]+append : f.to_s }.join(', ').gsub(/, ([^,]+)$/, ' or \1')
51
51
  end
@@ -77,24 +77,24 @@ module Travis::Yaml
77
77
  end
78
78
 
79
79
  class Notifications::Notification
80
- def self.spec_notification_format
81
- "list of strings or encrypted strings"
82
- end
83
-
84
- def self.spec_format
85
- super + ", or #{spec_notification_format}, or boolean value"
86
- end
80
+ def self.spec_format_prefix(input, append = "")
81
+ super(input)
82
+ input << ", or boolean value#{append}"
83
+ end
87
84
  end
88
85
 
89
- class Notifications::Flowdoc
90
- def self.spec_notification_format
91
- "string, encrypted string"
92
- end
86
+ class Notifications::Flowdock
87
+ def self.spec_format_prefix(input, append = "")
88
+ input << ", or string#{append}, encrypted string#{append}, or boolean value#{append}"
89
+ end
93
90
  end
94
91
 
95
92
  class Sequence
96
- def self.spec_format
97
- "list of " << type.spec_format("s") << "; or a single " << type.spec_format if type.spec_format
93
+ def self.spec_format(append = "", children = true, *)
94
+ return unless type.spec_format
95
+ format = "list of " << type.spec_format("s")
96
+ format << "; or a single " << type.spec_format if children
97
+ format
98
98
  end
99
99
 
100
100
  def self.spec(*prefix, **options)
@@ -117,8 +117,15 @@ module Travis::Yaml
117
117
  end
118
118
 
119
119
  class Mapping
120
- def self.spec_format(append = "")
121
- "key value mapping#{append}"
120
+ def self.spec_format(append = "", *)
121
+ format = "key value mapping#{append}"
122
+ spec_format_prefix(format, append)
123
+ format
124
+ end
125
+
126
+ def self.spec_format_prefix(input, append = "")
127
+ input << ", or #{subnode_for(prefix_sequence).spec_format('s', prefix_sequence != prefix_scalar)}" if prefix_sequence
128
+ input << ", or #{subnode_for(prefix_scalar).spec_format(append)}" if prefix_scalar and prefix_scalar != prefix_sequence
122
129
  end
123
130
 
124
131
  def self.default_spec_description
@@ -0,0 +1,57 @@
1
+ describe Travis::Yaml::Matrix do
2
+ let(:matrix) { config.to_matrix }
3
+ let(:entries) { matrix.entries }
4
+ let(:matrix_attributes) { entries.first.matrix_attributes }
5
+
6
+ context 'no matrix' do
7
+ let(:config) { Travis::Yaml.load(language: "ruby") }
8
+ specify { expect(matrix.size) .to be == 1 }
9
+ specify { expect(entries.first.to_ruby) .to be == {"language"=>"ruby", "os"=>["linux"]} }
10
+ specify { expect(config.to_ruby) .to be == {"language"=>"ruby", "os"=>["linux"]} }
11
+ specify { expect(entries.first) .to be_a(Travis::Yaml::Matrix::Entry) }
12
+ specify { expect(matrix_attributes) .to be_empty }
13
+ specify { expect(matrix.axes) .to be_empty }
14
+ end
15
+
16
+ context 'simple matrix' do
17
+ let(:config) { Travis::Yaml.load(ruby: ["ruby", "jruby"]) }
18
+ specify { expect(matrix.size) .to be == 2 }
19
+ specify { expect(entries.first.to_ruby) .to be == {"language"=>"ruby", "os"=>["linux"], "ruby"=>"ruby"} }
20
+ specify { expect(config.to_ruby) .to be == {"language"=>"ruby", "os"=>["linux"], "ruby"=>["ruby", "jruby"]} }
21
+ specify { expect(entries.first) .to be_a(Travis::Yaml::Matrix::Entry) }
22
+ specify { expect(matrix_attributes) .to be == { ruby: "ruby"} }
23
+ specify { expect(matrix.axes) .to be == [:ruby] }
24
+ end
25
+
26
+ context 'two dimensional matrix' do
27
+ let(:config) { Travis::Yaml.load(ruby: ["ruby", "jruby"], os: ["linux", "osx"]) }
28
+ specify { expect(matrix.size) .to be == 4 }
29
+ specify { expect(entries.first.to_ruby) .to be == {"language"=>"ruby", "os"=>"linux", "ruby"=>"ruby"} }
30
+ specify { expect(config.to_ruby) .to be == {"language"=>"ruby", "os"=>["linux", "osx"], "ruby"=>["ruby", "jruby"]} }
31
+ specify { expect(entries.first) .to be_a(Travis::Yaml::Matrix::Entry) }
32
+ specify { expect(matrix_attributes) .to be == { ruby: "ruby", os: "linux" } }
33
+ specify { expect(matrix.axes) .to be == [:ruby, :os] }
34
+ end
35
+
36
+ context 'matrix env, no global env' do
37
+ let(:config) { Travis::Yaml.load(env: ['a', 'b']) }
38
+ specify { expect(matrix.size) .to be == 2 }
39
+ specify { expect(entries.first.to_ruby) .to be == {"env"=>{"global"=>["a"]}, "language"=>"ruby", "os"=>["linux"]} }
40
+ specify { expect(entries.last.to_ruby) .to be == {"env"=>{"global"=>["b"]}, "language"=>"ruby", "os"=>["linux"]} }
41
+ specify { expect(config.to_ruby) .to be == {"env"=>{"matrix"=>["a", "b"]}, "language"=>"ruby", "os"=>["linux"]} }
42
+ specify { expect(entries.first) .to be_a(Travis::Yaml::Matrix::Entry) }
43
+ specify { expect(matrix_attributes) .to be == { env: "a" } }
44
+ specify { expect(matrix.axes) .to be == [:env] }
45
+ end
46
+
47
+ context 'matrix env, global env' do
48
+ let(:config) { Travis::Yaml.load(env: { matrix: ['a', 'b'], global: ['x'] }) }
49
+ specify { expect(matrix.size) .to be == 2 }
50
+ specify { expect(entries.first.to_ruby) .to be == {"env"=>{"global"=>["x", "a"]}, "language"=>"ruby", "os"=>["linux"]} }
51
+ specify { expect(entries.last.to_ruby) .to be == {"env"=>{"global"=>["x", "b"]}, "language"=>"ruby", "os"=>["linux"]} }
52
+ specify { expect(config.to_ruby) .to be == {"env"=>{"matrix"=>["a", "b"], "global"=>["x"]}, "language"=>"ruby", "os"=>["linux"]} }
53
+ specify { expect(entries.first) .to be_a(Travis::Yaml::Matrix::Entry) }
54
+ specify { expect(matrix_attributes) .to be == { env: "a" } }
55
+ specify { expect(matrix.axes) .to be == [:env] }
56
+ end
57
+ end
@@ -0,0 +1,63 @@
1
+ describe Travis::Yaml::Nodes::Addons do
2
+ context 'from Ruby' do
3
+ def addons(input)
4
+ Travis::Yaml.parse!(language: 'ruby', addons: input).addons
5
+ end
6
+
7
+ context 'artifacts' do
8
+ let :config do
9
+ addons(artifacts: {
10
+ bucket: 'whatever',
11
+ branch: 'borken',
12
+ concurrency: 40,
13
+ debug: 1,
14
+ key: 'foo',
15
+ max_size: 1024 * 1024 * 10,
16
+ paths: '$(git ls-files -o | tr "\n" ":")',
17
+ secret: 'bar',
18
+ target_paths: 'somewhere/in/teh/clood',
19
+ log_format: 'special',
20
+ })
21
+ end
22
+
23
+ example { expect(config.artifacts.key).to be == 'foo' }
24
+ example { expect(config.artifacts.secret).to be == 'bar' }
25
+ example { expect(config.artifacts.bucket).to be == 'whatever' }
26
+ end
27
+
28
+ context 'code_climate' do
29
+ example { expect(addons(code_climate: true).code_climate).to be == {} }
30
+ example { expect(addons(code_climate: { repo_token: "foo" }).code_climate.repo_token).to be == "foo" }
31
+ end
32
+
33
+ context 'coverity_scan' do
34
+ example do
35
+ config = addons(coverity_scan: { project: { name: :foo } })
36
+ expect(config.coverity_scan.project.name).to be == "foo"
37
+ end
38
+ end
39
+
40
+ context 'firefox' do
41
+ example { expect(addons(firefox: '15').firefox).to be == '15' }
42
+ end
43
+
44
+ context 'hosts' do
45
+ example { expect(addons(hosts: 'foo.dev').hosts).to be == ['foo.dev'] }
46
+ example { expect(addons(hosts: ['foo.dev', 'bar.dev']).hosts).to be == ['foo.dev', 'bar.dev'] }
47
+ end
48
+
49
+ context 'postgresql' do
50
+ example { expect(addons(postgresql: '9.1').postgresql).to be == '9.1' }
51
+ end
52
+
53
+ context 'sauce_connect' do
54
+ example { expect(addons(sauce_connect: true).sauce_connect).to be == {} }
55
+ example { expect(addons(sauce_connect: { username: "foo" }).sauce_connect.username).to be == "foo" }
56
+ end
57
+
58
+ context 'ssh_known_hosts' do
59
+ example { expect(addons(ssh_known_hosts: 'git.example.org').ssh_known_hosts).to be == ['git.example.org'] }
60
+ example { expect(addons(ssh_known_hosts: ['git.example.org', 'git.example.com']).ssh_known_hosts).to be == ['git.example.org', 'git.example.com'] }
61
+ end
62
+ end
63
+ end
@@ -2,12 +2,12 @@ describe Travis::Yaml::Nodes::Cache do
2
2
  context 'from Ruby' do
3
3
  specify 'with true' do
4
4
  config = Travis::Yaml.parse(cache: true)
5
- expect(config.cache).to be == { 'apt' => true, 'bundler' => true }
5
+ expect(config.cache).to be == { 'apt' => true, 'bundler' => true, 'cocoapods' => true }
6
6
  end
7
7
 
8
8
  specify 'with false' do
9
9
  config = Travis::Yaml.parse(cache: false)
10
- expect(config.cache).to be == { 'apt' => false, 'bundler' => false }
10
+ expect(config.cache).to be == { 'apt' => false, 'bundler' => false, 'cocoapods' => false }
11
11
  end
12
12
 
13
13
  specify 'with string' do
@@ -29,12 +29,12 @@ describe Travis::Yaml::Nodes::Cache do
29
29
  context 'from YAML' do
30
30
  specify 'with true' do
31
31
  config = Travis::Yaml.parse('cache: on')
32
- expect(config.cache).to be == { 'apt' => true, 'bundler' => true }
32
+ expect(config.cache).to be == { 'apt' => true, 'bundler' => true, 'cocoapods' => true }
33
33
  end
34
34
 
35
35
  specify 'with false' do
36
36
  config = Travis::Yaml.parse('cache: off')
37
- expect(config.cache).to be == { 'apt' => false, 'bundler' => false }
37
+ expect(config.cache).to be == { 'apt' => false, 'bundler' => false, 'cocoapods' => false }
38
38
  end
39
39
 
40
40
  specify 'with string' do
@@ -46,6 +46,18 @@ describe Travis::Yaml::Nodes::Deploy do
46
46
  config = Travis::Yaml.parse(deploy: { provider: :heroku, on: { rvm: "2.0.0", repo: 'foo/bar', tags: true } })
47
47
  expect(config.deploy.first.on).to be == { 'ruby' => '2.0.0', 'repo' => 'foo/bar', 'tags' => true }
48
48
  end
49
+
50
+ specify 'with branch specific settings' do
51
+ config = Travis::Yaml.parse(deploy: { provider: :heroku, foo: { master: :bar, staging: :baz } })
52
+ expect(config.deploy.first['foo']) .to be == { 'master' => 'bar', 'staging' => 'baz' }
53
+ expect(config.deploy.nested_warnings) .to be_empty
54
+ end
55
+
56
+ specify 'branches in settings that are not in the condition' do
57
+ config = Travis::Yaml.parse(deploy: { provider: :heroku, foo: { master: :bar, staging: :baz }, on: :master })
58
+ expect(config.deploy.first['foo']) .to be == { 'master' => 'bar' }
59
+ expect(config.deploy.nested_warnings) .to include([['foo'], 'branch "staging" not permitted by deploy condition, dropping'])
60
+ end
49
61
  end
50
62
 
51
63
  describe 'from yaml' do
@@ -0,0 +1,11 @@
1
+ describe Travis::Yaml::Nodes::Dist do
2
+ it 'adds warnings about feature' do
3
+ expect(Travis::Yaml.parse(language: 'ruby', dist: 'precise').warnings).
4
+ to include('your repository must be feature flagged for the "dist" setting to be used')
5
+ end
6
+
7
+ specify 'set dist value' do
8
+ config = Travis::Yaml.parse(dist: 'trusty')
9
+ expect(config.dist).to eq 'trusty'
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ describe Travis::Yaml::Nodes::Deploy do
2
+ context 'from Ruby' do
3
+ specify 'list of values' do
4
+ config = Travis::Yaml.parse <<-YAML.gsub(/^ {8}/, '')
5
+ ---
6
+ language: ruby
7
+ env:
8
+ - "FOO=BAR"
9
+ - "FOX=BAZ"
10
+ YAML
11
+
12
+ expect(config.nested_warnings).to be_empty
13
+ expect(config.env.matrix.length).to be == 2
14
+ end
15
+
16
+ specify 'global with secure' do
17
+ config = Travis::Yaml.parse <<-YAML.gsub(/^ {8}/, '')
18
+ ---
19
+ language: ruby
20
+ env:
21
+ global:
22
+ - secure: "..."
23
+ - secure: "..."
24
+ YAML
25
+
26
+ expect(config.nested_warnings).to be_empty
27
+ expect(config.env.global.length).to be == 2
28
+ end
29
+
30
+ specify 'global and matrix' do
31
+ config = Travis::Yaml.parse <<-YAML.gsub(/^ {8}/, '')
32
+ ---
33
+ language: ruby
34
+ env:
35
+ matrix:
36
+ - "FOO=BAR"
37
+ - "FOX=BAZ"
38
+ global:
39
+ - secure: "..."
40
+ - secure: "..."
41
+ YAML
42
+
43
+ expect(config.nested_warnings).to be_empty
44
+ expect(config.env.global.length).to be == 2
45
+ expect(config.env.matrix.length).to be == 2
46
+ end
47
+ end
48
+ end
@@ -48,7 +48,7 @@ describe Travis::Yaml::Nodes::Git do
48
48
  expect(Travis::Yaml.parse('git: { depth: !str foo }').nested_warnings).to \
49
49
  include([['git'], 'dropping "depth" section: "str" not supported, dropping "foo"'])
50
50
  expect(Travis::Yaml.parse('git: { depth: foo }').nested_warnings).to \
51
- include([['git'], 'dropping "depth" section: failed to parse "int" - invalid value for Integer(): "foo"'])
51
+ include([['git'], 'dropping "depth" section: failed to parse "int" - invalid value for Integer: "foo"'])
52
52
  end
53
53
  end
54
54
  end
@@ -0,0 +1,11 @@
1
+ describe Travis::Yaml::Nodes::Group do
2
+ it 'adds warnings about feature' do
3
+ expect(Travis::Yaml.parse(group: 'dev').warnings).
4
+ to include('your repository must be feature flagged for the "group" setting to be used')
5
+ end
6
+
7
+ specify 'set group value' do
8
+ config = Travis::Yaml.parse(group: 'update')
9
+ expect(config.group).to eq 'update'
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ describe Travis::Yaml do
2
+ context 'from ruby' do
3
+ specify 'valid versions' do
4
+ config = Travis::Yaml.parse(node_js: ['0.10', '0.8.0'], language: 'node.js')
5
+ expect(config.node_js).to be == ['0.10', '0.8.0']
6
+ end
7
+
8
+ specify 'invalid versions' do
9
+ config = Travis::Yaml.parse(node_js: ['0.10.x', '0.8.0'], language: 'node.js')
10
+ expect(config.node_js).to be == ['0.8.0']
11
+ expect(config.nested_warnings).to include([['node_js'], 'value "0.10.x" is not a valid version'])
12
+ end
13
+ end
14
+ end
@@ -42,4 +42,11 @@ describe Travis::Yaml::Nodes::Notifications do
42
42
  expect(config.notifications.email).to be_enabled
43
43
  end
44
44
  end
45
+
46
+ context :flowdoc do
47
+ example "handles scalar value" do
48
+ config = Travis::Yaml.parse(notifications: { flowdock: "foo" })
49
+ expect(config.notifications.flowdock.api_token).to be == "foo"
50
+ end
51
+ end
45
52
  end