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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +14 -0
  5. data/Gemfile +9 -0
  6. data/Gemfile.lock +73 -0
  7. data/LICENSE +22 -0
  8. data/README.md +232 -0
  9. data/Rakefile +3 -0
  10. data/SPEC.md +1018 -0
  11. data/bench/parser_bench.rb +54 -0
  12. data/config.ru +2 -0
  13. data/lib/travis/yaml.rb +43 -0
  14. data/lib/travis/yaml/matrix.rb +65 -0
  15. data/lib/travis/yaml/nodes.rb +40 -0
  16. data/lib/travis/yaml/nodes/branches.rb +12 -0
  17. data/lib/travis/yaml/nodes/bundler_args.rb +6 -0
  18. data/lib/travis/yaml/nodes/cache.rb +29 -0
  19. data/lib/travis/yaml/nodes/compiler.rb +7 -0
  20. data/lib/travis/yaml/nodes/compiler_entry.rb +9 -0
  21. data/lib/travis/yaml/nodes/deploy.rb +7 -0
  22. data/lib/travis/yaml/nodes/deploy_conditions.rb +12 -0
  23. data/lib/travis/yaml/nodes/deploy_entry.rb +10 -0
  24. data/lib/travis/yaml/nodes/env.rb +36 -0
  25. data/lib/travis/yaml/nodes/fixed_value.rb +60 -0
  26. data/lib/travis/yaml/nodes/git.rb +9 -0
  27. data/lib/travis/yaml/nodes/jdk.rb +11 -0
  28. data/lib/travis/yaml/nodes/language.rb +18 -0
  29. data/lib/travis/yaml/nodes/language_specific.rb +45 -0
  30. data/lib/travis/yaml/nodes/mapping.rb +204 -0
  31. data/lib/travis/yaml/nodes/matrix.rb +36 -0
  32. data/lib/travis/yaml/nodes/node.rb +102 -0
  33. data/lib/travis/yaml/nodes/notifications.rb +77 -0
  34. data/lib/travis/yaml/nodes/open_mapping.rb +18 -0
  35. data/lib/travis/yaml/nodes/os.rb +21 -0
  36. data/lib/travis/yaml/nodes/os_entry.rb +19 -0
  37. data/lib/travis/yaml/nodes/root.rb +44 -0
  38. data/lib/travis/yaml/nodes/ruby.rb +11 -0
  39. data/lib/travis/yaml/nodes/scalar.rb +97 -0
  40. data/lib/travis/yaml/nodes/sequence.rb +84 -0
  41. data/lib/travis/yaml/nodes/stage.rb +6 -0
  42. data/lib/travis/yaml/nodes/version.rb +6 -0
  43. data/lib/travis/yaml/nodes/version_list.rb +7 -0
  44. data/lib/travis/yaml/nodes/virtual_env.rb +7 -0
  45. data/lib/travis/yaml/parser.rb +31 -0
  46. data/lib/travis/yaml/parser/dummy.rb +13 -0
  47. data/lib/travis/yaml/parser/psych.rb +217 -0
  48. data/lib/travis/yaml/parser/ruby.rb +77 -0
  49. data/lib/travis/yaml/secure_string.rb +12 -0
  50. data/lib/travis/yaml/version.rb +5 -0
  51. data/play/lint.rb +8 -0
  52. data/play/spec.rb +183 -0
  53. data/play/weblint.rb +296 -0
  54. data/spec/nodes/.rb +0 -0
  55. data/spec/nodes/branches_spec.rb +45 -0
  56. data/spec/nodes/bundler_args_spec.rb +9 -0
  57. data/spec/nodes/cache_spec.rb +55 -0
  58. data/spec/nodes/compiler_spec.rb +14 -0
  59. data/spec/nodes/deploy_spec.rb +83 -0
  60. data/spec/nodes/git_spec.rb +55 -0
  61. data/spec/nodes/jdk_spec.rb +41 -0
  62. data/spec/nodes/language_spec.rb +79 -0
  63. data/spec/nodes/notifications_spec.rb +45 -0
  64. data/spec/nodes/os_spec.rb +28 -0
  65. data/spec/nodes/ruby_spec.rb +69 -0
  66. data/spec/nodes/stage_spec.rb +34 -0
  67. data/spec/nodes/virtual_env_spec.rb +9 -0
  68. data/spec/parser/dummy_spec.rb +7 -0
  69. data/spec/parser/ruby_spec.rb +41 -0
  70. data/spec/support.rb +3 -0
  71. data/spec/support/coverage.rb +11 -0
  72. data/spec/support/environment.rb +1 -0
  73. data/spec/yaml_spec.rb +26 -0
  74. data/travis-yaml.gemspec +24 -0
  75. metadata +207 -0
@@ -0,0 +1,54 @@
1
+ require 'bundler/setup'
2
+ require 'benchmark'
3
+ require 'psych'
4
+ require 'safe_yaml/load'
5
+ require 'travis/yaml'
6
+
7
+ parsers = [Psych, SafeYAML, Travis::Yaml]
8
+ content = <<-YAML
9
+ # from rails/rails
10
+ script: 'ci/travis.rb'
11
+ before_install:
12
+ - travis_retry gem install bundler
13
+ - "rvm current | grep 'jruby' && export AR_JDBC=true || echo"
14
+ rvm:
15
+ - 1.9.3
16
+ - 2.0.0
17
+ - 2.1.1
18
+ - rbx-2
19
+ - jruby
20
+ env:
21
+ - "GEM=railties"
22
+ - "GEM=ap,am,amo,as,av"
23
+ - "GEM=ar:mysql"
24
+ - "GEM=ar:mysql2"
25
+ - "GEM=ar:sqlite3"
26
+ - "GEM=ar:postgresql"
27
+ matrix:
28
+ allow_failures:
29
+ - rvm: rbx-2
30
+ - rvm: jruby
31
+ fast_finish: true
32
+ notifications:
33
+ email: false
34
+ irc:
35
+ on_success: change
36
+ on_failure: always
37
+ channels:
38
+ - "irc.freenode.org#rails-contrib"
39
+ campfire:
40
+ on_success: change
41
+ on_failure: always
42
+ bundler_args: --path vendor/bundle --without test
43
+ services:
44
+ - memcached
45
+ YAML
46
+
47
+ GC.disable
48
+ Benchmark.bmbm do |x|
49
+ parsers.each do |parser|
50
+ x.report(parser.inspect) do
51
+ 500.times { parser.load(content) }
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,2 @@
1
+ require_relative './play/weblint'
2
+ run Sinatra::Application
@@ -0,0 +1,43 @@
1
+ module Travis
2
+ module Yaml
3
+ Error ||= Class.new(StandardError)
4
+ ParseError ||= Class.new(Error)
5
+
6
+ require 'travis/yaml/secure_string'
7
+ require 'travis/yaml/nodes'
8
+ require 'travis/yaml/matrix'
9
+ require 'travis/yaml/parser'
10
+
11
+ extend self
12
+
13
+ def parse(value)
14
+ Parser.parse(value)
15
+ end
16
+
17
+ alias_method :load, :parse
18
+
19
+ def parse!(value, file_name = '.travis.yml')
20
+ result = parse(value)
21
+ result.nested_warnings.each do |key, message|
22
+ warn key.empty? ? "#{file_name}: #{message}" :
23
+ "#{file_name}: #{key.join(?.)} section - #{message}"
24
+ end
25
+ result
26
+ end
27
+
28
+ def new
29
+ root = Nodes::Root.new
30
+ yield root if block_given?
31
+ root.deep_verify
32
+ root
33
+ end
34
+
35
+ def matrix(value)
36
+ Matrix.new parse(value)
37
+ end
38
+
39
+ def matrix!(value, file_name = '.travis.yml')
40
+ Matrix.new parse!(value, file_name)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,65 @@
1
+ require 'delegate'
2
+
3
+ module Travis::Yaml
4
+ class Matrix < DelegateClass(Array)
5
+ EXPAND_KEYS = [
6
+ :compiler, :gemfile, :ghc, :go, :jdk, :lein, :node_js, :otp_release,
7
+ :perl, :php, :python, :ruby, :scala, :xcode_scheme, :xcode_sdk, :os
8
+ ]
9
+
10
+ KEYS = EXPAND_KEYS + [:env]
11
+
12
+ class Entry < DelegateClass(Nodes::Root)
13
+ attr_reader :env, :matrix_attributes
14
+
15
+ def initialize(root, matrix_attributes)
16
+ super(root)
17
+
18
+ @matrix_attributes = matrix_attributes
19
+ @env = Nodes::Env.new(self)
20
+ inherited_env = root.env.global if root.env
21
+ @env.global = [matrix_attributes[:env], *inherited_env].compact
22
+ end
23
+
24
+ EXPAND_KEYS.each do |key|
25
+ define_method(key) { @matrix_attributes.fetch(key, super()) }
26
+ end
27
+
28
+ def inspect
29
+ "#<#{self.class}: #{matrix_attributes}>"
30
+ end
31
+ end
32
+
33
+ attr_reader :root
34
+
35
+ def initialize(root)
36
+ @root = root
37
+ super(entries)
38
+ end
39
+
40
+ def axes
41
+ @axes ||= KEYS.select do |key|
42
+ next true if values_for(key) and values_for(key).size > 1
43
+ root.matrix.include.any? { |i| i[key] } if root.matrix and root.matrix.include
44
+ end
45
+ end
46
+
47
+ def entries
48
+ @entries ||= begin
49
+ first, *rest = axes.map { |k| values_for(k) || [nil] }
50
+ entries = Array(first).product(*rest).map { |list| Hash[axes.zip(list)] }
51
+ if m = root.matrix
52
+ entries.delete_if { |e| m.exclude.any? { |p| p.all? { |k,v| e[k.to_sym] == v } } } if m.exclude
53
+ m.include.each { |i| entries << Hash[axes.map { |k| [k, i[k]] }] } if m.include
54
+ end
55
+ entries.map! { |attributes| Entry.new(root, attributes) }
56
+ entries.any? ? entries : [root]
57
+ end
58
+ end
59
+
60
+ def values_for(key)
61
+ return root[key] unless key == :env
62
+ root.env.matrix if root.env
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,40 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ def self.[](key)
4
+ return key if key.respond_to? :new
5
+ name = constants.detect { |c| c.downcase == key }
6
+ raise ArgumentError, "unknown node type %p" % key unless name
7
+ const_get(name)
8
+ end
9
+
10
+ require 'travis/yaml/nodes/node'
11
+ require 'travis/yaml/nodes/scalar'
12
+ require 'travis/yaml/nodes/fixed_value'
13
+ require 'travis/yaml/nodes/sequence'
14
+ require 'travis/yaml/nodes/mapping'
15
+ require 'travis/yaml/nodes/open_mapping'
16
+ require 'travis/yaml/nodes/language_specific'
17
+ require 'travis/yaml/nodes/version'
18
+ require 'travis/yaml/nodes/version_list'
19
+ require 'travis/yaml/nodes/git'
20
+ require 'travis/yaml/nodes/bundler_args'
21
+ require 'travis/yaml/nodes/stage'
22
+ require 'travis/yaml/nodes/deploy_conditions'
23
+ require 'travis/yaml/nodes/deploy_entry'
24
+ require 'travis/yaml/nodes/deploy'
25
+ require 'travis/yaml/nodes/language'
26
+ require 'travis/yaml/nodes/compiler_entry'
27
+ require 'travis/yaml/nodes/compiler'
28
+ require 'travis/yaml/nodes/os_entry'
29
+ require 'travis/yaml/nodes/os'
30
+ require 'travis/yaml/nodes/virtual_env'
31
+ require 'travis/yaml/nodes/ruby'
32
+ require 'travis/yaml/nodes/jdk'
33
+ require 'travis/yaml/nodes/env'
34
+ require 'travis/yaml/nodes/matrix'
35
+ require 'travis/yaml/nodes/notifications'
36
+ require 'travis/yaml/nodes/branches'
37
+ require 'travis/yaml/nodes/cache'
38
+ require 'travis/yaml/nodes/root'
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Branches < Mapping
4
+ class Matcher < Sequence
5
+ type Scalar[:str, :regexp]
6
+ end
7
+
8
+ map :only, :except, to: Matcher
9
+ auto_prefix :only
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class BundlerArgs < Scalar
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,29 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Cache < Mapping
4
+ map :apt, :bundler, to: Scalar[:bool]
5
+ map :directories, to: Sequence
6
+
7
+ def visit_scalar(visitor, type, value, implicit = true)
8
+ case type
9
+ when :bool
10
+ visit_key_value(visitor, :bundler, value)
11
+ visit_key_value(visitor, :apt, value)
12
+ when :str
13
+ key = visitor.generate_key(self, value)
14
+ self[key] = true
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def visit_sequence(visitor, value)
21
+ visitor.apply_sequence(self, value)
22
+ end
23
+
24
+ def visit_child(visitor, value)
25
+ visit_scalar(visitor, :str, value)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Compiler < Sequence
4
+ type CompilerEntry
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class CompilerEntry < FixedValue
4
+ ignore_case
5
+ default :gcc
6
+ value :gcc, :clang, :"g++" => :gcc, :"clang++" => :clang
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Deploy < Sequence
4
+ type DeployEntry
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class DeployConditions < Mapping
4
+ include LanguageSpecific
5
+ map :jdk, :node, :perl, :php, :python, :ruby, :scala, :node, to: Version
6
+ map :rvm, to: :ruby
7
+ map :repo, :branch, :condition, to: Scalar[:str]
8
+ map :all_branches, :tags, to: Scalar[:bool]
9
+ prefix_scalar :branch
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class DeployEntry < OpenMapping
4
+ prefix_scalar :provider
5
+ map :provider, to: Scalar, required: true
6
+ map :edge, to: Scalar[:bool], experimental: true
7
+ map :on, to: DeployConditions
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,36 @@
1
+ require 'shellwords'
2
+
3
+ module Travis::Yaml
4
+ module Nodes
5
+ class Env < Mapping
6
+ class Variables < Scalar
7
+ cast :str, :secure
8
+
9
+ def visit_mapping(visitor, value)
10
+ self.value = ""
11
+ visitor.apply_mapping(self, value)
12
+ end
13
+
14
+ def visit_mapping(visitor, key, value)
15
+ key = visitor.generate_key(self, key)
16
+ node = Scalar.new
17
+ visitor.accept(node, value)
18
+
19
+ if node.errors?
20
+ warning "dropping %p: %s", key, value.errors.join(', ')
21
+ else
22
+ self.value << " " unless self.value.empty?
23
+ self.value << "#{key}=#{Shellwords.escape(value.value)}"
24
+ end
25
+ end
26
+ end
27
+
28
+ class List < Sequence
29
+ type Variables
30
+ end
31
+
32
+ map :global, :matrix, to: List
33
+ auto_prefix :matrix
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,60 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class FixedValue < Scalar
4
+ def self.[](*values)
5
+ Class.new(self) { value(*values) }
6
+ end
7
+
8
+ def self.default(value = nil)
9
+ value &&= value.to_s
10
+ super(value)
11
+ end
12
+
13
+ def self.ignore_case?
14
+ @ignore_case ||= false
15
+ end
16
+
17
+ def self.ignore_case
18
+ @ignore_case = true
19
+ end
20
+
21
+ def self.valid_values
22
+ @valid_values ||= superclass.respond_to?(:valid_values) ? superclass.valid_values.dup : []
23
+ end
24
+
25
+ def self.aliases
26
+ @aliases ||= superclass.respond_to?(:aliases) ? superclass.aliases.dup : {}
27
+ end
28
+
29
+ def self.map_value(value)
30
+ value = value.to_s
31
+ if ignore_case?
32
+ all_values = valid_values + aliases.keys
33
+ value = all_values.detect { |supported| supported.downcase == value.downcase }
34
+ end
35
+ value &&= aliases.fetch(value, value)
36
+ value if valid_values.include? value
37
+ end
38
+
39
+ def self.value(*list)
40
+ list.each do |value|
41
+ if value.respond_to? :each_pair
42
+ value.each_pair { |aka, proper| aliases[aka.to_s] = proper.to_s }
43
+ else
44
+ valid_values << value.to_s
45
+ end
46
+ end
47
+ end
48
+
49
+ def value=(value)
50
+ if mapped = self.class.map_value(value)
51
+ super(mapped)
52
+ elsif self.value
53
+ warning "illegal value %p, defaulting to %p", value, self.value
54
+ elsif value
55
+ error "illegal value %p", value
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,9 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Git < Mapping
4
+ map :depth, to: Scalar[:int]
5
+ map :submodules, to: Scalar[:bool]
6
+ map :strategy, to: FixedValue[:clone, :tarball]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class JDK < VersionList
4
+ def verify_language(language)
5
+ return unless language == 'ruby'
6
+ return if @parent.ruby and @parent.ruby.jruby?
7
+ error 'specified "jdk", but "ruby" does not include "jruby"'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module Travis::Yaml
2
+ module Nodes
3
+ class Language < FixedValue
4
+ ignore_case
5
+ default :ruby
6
+
7
+ value :c, :cpp, :clojure, :erlang, :go, :groovy, :haskell, :java, :node_js,
8
+ :"objective-c", :ruby, :python, :perl, :php, :scala, :android
9
+ value jvm: :java, javascript: :node_js, node: :node_js, nodejs: :node_js, golang: :go,
10
+ objective_c: :"objective-c", obj_c: :"objective-c", objc: :"objective-c"
11
+ value "c++" => :cpp, "node.js" => :node_js, "obj-c" => :"objective-c"
12
+
13
+ def default_os
14
+ value == "objective-c" ? :osx : OSEntry.default
15
+ end
16
+ end
17
+ end
18
+ end