highway 0.0.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/lib/highway.rb +8 -4
  3. data/lib/highway/compiler/analyze/analyzer.rb +249 -0
  4. data/lib/highway/compiler/analyze/tree/root.rb +95 -0
  5. data/lib/highway/compiler/analyze/tree/segments/text.rb +36 -0
  6. data/lib/highway/compiler/analyze/tree/segments/variable.rb +43 -0
  7. data/lib/highway/compiler/analyze/tree/stage.rb +48 -0
  8. data/lib/highway/compiler/analyze/tree/step.rb +69 -0
  9. data/lib/highway/compiler/analyze/tree/values/array.rb +45 -0
  10. data/lib/highway/compiler/analyze/tree/values/base.rb +67 -0
  11. data/lib/highway/compiler/analyze/tree/values/hash.rb +45 -0
  12. data/lib/highway/compiler/analyze/tree/values/primitive.rb +43 -0
  13. data/lib/highway/compiler/analyze/tree/variable.rb +48 -0
  14. data/lib/highway/compiler/build/builder.rb +154 -0
  15. data/lib/highway/compiler/build/output/invocation.rb +70 -0
  16. data/lib/highway/compiler/build/output/manifest.rb +52 -0
  17. data/lib/highway/compiler/parse/parser.rb +92 -0
  18. data/lib/highway/compiler/parse/tree/root.rb +73 -0
  19. data/lib/highway/compiler/parse/tree/step.rb +62 -0
  20. data/lib/highway/compiler/parse/tree/variable.rb +48 -0
  21. data/lib/highway/compiler/parse/versions/v1.rb +110 -0
  22. data/lib/highway/compiler/suite.rb +56 -0
  23. data/lib/highway/environment.rb +282 -0
  24. data/lib/highway/fastlane/action.rb +67 -0
  25. data/lib/highway/interface.rb +135 -0
  26. data/lib/highway/main.rb +173 -0
  27. data/lib/highway/runtime/context.rb +229 -0
  28. data/lib/highway/runtime/report.rb +80 -0
  29. data/lib/highway/runtime/runner.rb +286 -0
  30. data/lib/highway/steps/infrastructure.rb +20 -0
  31. data/lib/highway/steps/library/action.rb +42 -0
  32. data/lib/highway/steps/library/appcenter.rb +106 -0
  33. data/lib/highway/steps/library/appstore.rb +137 -0
  34. data/lib/highway/steps/library/carthage.rb +67 -0
  35. data/lib/highway/steps/library/cocoapods.rb +76 -0
  36. data/lib/highway/steps/library/copy_artifacts.rb +36 -0
  37. data/lib/highway/steps/library/lane.rb +42 -0
  38. data/lib/highway/steps/library/sh.rb +36 -0
  39. data/lib/highway/steps/library/slack.rb +381 -0
  40. data/lib/highway/steps/library/testflight.rb +105 -0
  41. data/lib/highway/steps/library/xcode_archive.rb +162 -0
  42. data/lib/highway/steps/library/xcode_test.rb +264 -0
  43. data/lib/highway/steps/parameters/base.rb +52 -0
  44. data/lib/highway/steps/parameters/compound.rb +141 -0
  45. data/lib/highway/steps/parameters/single.rb +74 -0
  46. data/lib/highway/steps/registry.rb +92 -0
  47. data/lib/highway/steps/step.rb +52 -0
  48. data/lib/highway/steps/types/any.rb +65 -0
  49. data/lib/highway/steps/types/anyof.rb +64 -0
  50. data/lib/highway/steps/types/array.rb +44 -0
  51. data/lib/highway/steps/types/bool.rb +36 -0
  52. data/lib/highway/steps/types/enum.rb +44 -0
  53. data/lib/highway/steps/types/hash.rb +45 -0
  54. data/lib/highway/steps/types/number.rb +38 -0
  55. data/lib/highway/steps/types/set.rb +39 -0
  56. data/lib/highway/steps/types/string.rb +47 -0
  57. data/lib/highway/steps/types/url.rb +35 -0
  58. data/lib/highway/utilities.rb +51 -0
  59. data/lib/highway/version.rb +9 -1
  60. metadata +194 -22
  61. data/.gitignore +0 -4
  62. data/Gemfile +0 -4
  63. data/Rakefile +0 -1
  64. data/highway.gemspec +0 -24
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8e7516548771a99c73bea486d466a28a3019e93729955a359a6cc004d2336b6f
4
+ data.tar.gz: '083baad25067f0dd1c23194c04fec6ec53e7478a6aca9e79d40661d5e99c9447'
5
+ SHA512:
6
+ metadata.gz: db98b371c18cb00198ff871ee8730373d90544bf880a2ccbccb361f1e9603363e6a48372882f1c966f5e413942841d9a53a6472da204338a04e5ad1a4d51e701
7
+ data.tar.gz: 37d1df04101159db5b871ef8144f857fd79f1499f8795fb42135dd0f141fefa2c60eceb3d59780c091fc1eea74085fdd1b0ddae8eb26e63b74e849342f9d82a4
data/lib/highway.rb CHANGED
@@ -1,5 +1,9 @@
1
- require "highway/version"
1
+ #
2
+ # highway.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "highway/fastlane/action"
2
7
 
3
- module Highway
4
- # Your code goes here...
5
- end
8
+ require "highway/main"
9
+ require "highway/version"
@@ -0,0 +1,249 @@
1
+ #
2
+ # analyzer.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "highway/compiler/analyze/tree/root"
7
+ require "highway/utilities"
8
+
9
+ module Highway
10
+ module Compiler
11
+ module Analyze
12
+
13
+ # This class is responsible for semantic analysis of a parse tree. This is
14
+ # the second phase of the compiler.
15
+ class Analyzer
16
+
17
+ public
18
+
19
+ # Initialize an instance.
20
+ #
21
+ # @param registry [Highway::Steps::Registry] The steps registry.
22
+ # @param reporter [Highway::Interface] The interface.
23
+ def initialize(registry:, interface:)
24
+ @registry = registry
25
+ @interface = interface
26
+ end
27
+
28
+ # Analyze the parse tree.
29
+ #
30
+ # The semantic analyzer validates the parse tree in terms of content,
31
+ # performs segmentation of values and resolves steps against the
32
+ # registry.
33
+ #
34
+ # The semantic analyzer produces a semantic tree which is then used by
35
+ # build phase to generate a manifest.
36
+ #
37
+ # @param parse_tree [Highway::Compiler::Parse::Tree::Root] The parse tree.
38
+ #
39
+ # @return [Highway::Compiler::Analyze::Tree::Root]
40
+ def analyze(parse_tree:)
41
+
42
+ sema_tree = Analyze::Tree::Root.new()
43
+
44
+ sema_tree.add_stage(index: 0, name: "bootstrap", policy: :normal, )
45
+ sema_tree.add_stage(index: 1, name: "test", policy: :normal)
46
+ sema_tree.add_stage(index: 2, name: "deploy", policy: :normal)
47
+ sema_tree.add_stage(index: 3, name: "report", policy: :always)
48
+
49
+ sema_tree.default_preset = "default"
50
+
51
+ validate_preset_names(parse_tree: parse_tree)
52
+ validate_variable_names(parse_tree: parse_tree)
53
+ validate_variable_values(parse_tree: parse_tree)
54
+ validate_step_names(parse_tree: parse_tree)
55
+ validate_step_parameter_values(parse_tree: parse_tree)
56
+
57
+ resolve_variables(parse_tree: parse_tree, sema_tree: sema_tree)
58
+ resolve_steps(parse_tree: parse_tree, sema_tree: sema_tree)
59
+
60
+ validate_variable_references(sema_tree: sema_tree)
61
+
62
+ sema_tree
63
+
64
+ end
65
+
66
+ private
67
+
68
+ def validate_preset_names(parse_tree:)
69
+ parse_tree.variables.each do |variable|
70
+ assert_preset_name_valid(variable.preset, keypath: ["variables"])
71
+ end
72
+ parse_tree.steps.each do |step|
73
+ assert_preset_name_valid(step.preset, keypath: [step.stage])
74
+ end
75
+ end
76
+
77
+ def validate_variable_names(parse_tree:)
78
+ parse_tree.variables.each do |variable|
79
+ assert_variable_name_valid(variable.name, keypath: ["variables", variable.preset])
80
+ end
81
+ end
82
+
83
+ def validate_variable_values(parse_tree:)
84
+ parse_tree.variables.each do |variable|
85
+ assert_variable_value_valid(variable.value, keypath: ["variables", variable.preset, variable.name])
86
+ end
87
+ end
88
+
89
+ def validate_step_names(parse_tree:)
90
+ parse_tree.steps.each do |step|
91
+ assert_step_exists(step.name, keypath: [step.stage, step.preset])
92
+ end
93
+ end
94
+
95
+ def validate_step_parameter_values(parse_tree:)
96
+ parse_tree.steps.each do |step|
97
+ step.parameters.each_pair do |param_name, param_value|
98
+ assert_step_parameter_value_valid(param_value, keypath: [step.stage, step.preset, step.name, param_name])
99
+ end
100
+ end
101
+ end
102
+
103
+ def validate_variable_references(sema_tree:)
104
+ sema_tree.variables.each do |variable|
105
+ variable.value.select_variable_segments_with_scope(:static).each do |segment|
106
+ unless (ref_variable = find_referenced_variable(sema_tree: sema_tree, name: segment.name, preset: variable.preset))
107
+ @interface.fatal!("Unknown variable: '#{segment.name}' referenced from: '#{Utilities::keypath_to_s(["variables", variable.preset, variable.name])}'.")
108
+ end
109
+ if ref_variable.value.select_variable_segments_with_scope(:static).any? { |other| other.name == variable.name }
110
+ @interface.fatal!("Detected a reference cycle between: '#{Utilities::keypath_to_s(["variables", variable.preset, variable.name])}' and '#{Utilities::keypath_to_s(["variables", ref_variable.preset, ref_variable.name])}'.")
111
+ end
112
+ end
113
+ end
114
+ sema_tree.steps.each do |step|
115
+ step.parameters.children.each_pair do |name, value|
116
+ value.select_variable_segments_with_scope(:static).each do |segment|
117
+ unless find_referenced_variable(sema_tree: sema_tree, name: segment.name, preset: step.preset)
118
+ @interface.fatal!("Unknown variable: '#{segment.name}' referenced from: '#{Utilities::keypath_to_s([step.stage, step.preset, step.name, name])}'.")
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ def resolve_variables(parse_tree:, sema_tree:)
126
+ parse_tree.variables.each do |variable|
127
+ value = segmentize_value(variable.value)
128
+ sema_tree.add_variable(name: variable.name, value: value, preset: variable.preset)
129
+ end
130
+ end
131
+
132
+ def resolve_steps(parse_tree:, sema_tree:)
133
+ parse_tree.steps.each do |step|
134
+ klass = @registry.get_by_name(step.name)
135
+ parameters = segmentize_value(step.parameters)
136
+ sema_tree.add_step(index: step.index, name: step.name, step_class: klass, parameters: parameters, stage: step.stage, preset: step.preset)
137
+ end
138
+ end
139
+
140
+ def segmentize_value(value)
141
+ if value.is_a?(String)
142
+ Analyze::Tree::Values::Primitive.new(
143
+ value.to_enum(:scan, %r((?<!\\)\$\(([A-Z0-9:_]+)\)|((?:[^\\\$]|\\\$)+))).map { Regexp.last_match }.map { |match|
144
+ if match[1]
145
+ if match[1][0, 4] == "ENV:"
146
+ Analyze::Tree::Segments::Variable.new(match[1][4..-1], scope: :env)
147
+ else
148
+ Analyze::Tree::Segments::Variable.new(match[1], scope: :static)
149
+ end
150
+ elsif match[2]
151
+ Analyze::Tree::Segments::Text.new(match[2])
152
+ end
153
+ }
154
+ )
155
+ elsif value.is_a?(Array)
156
+ Analyze::Tree::Values::Array.new(
157
+ value.map { |element|
158
+ segmentize_value(element)
159
+ }
160
+ )
161
+ elsif value.is_a?(Hash)
162
+ Analyze::Tree::Values::Hash.new(
163
+ Utilities::hash_map(value) { |name, element|
164
+ [name, segmentize_value(element)]
165
+ }
166
+ )
167
+ elsif value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Numeric) || value.is_a?(NilClass)
168
+ Analyze::Tree::Values::Primitive.new(
169
+ [Analyze::Tree::Segments::Text.new(value.to_s)]
170
+ )
171
+ end
172
+ end
173
+
174
+ def find_referenced_variable(sema_tree:, name:, preset:)
175
+ sema_tree.variables.find do |variable|
176
+ variable.name == name && [preset, sema_tree.default_preset].include?(variable.preset)
177
+ end
178
+ end
179
+
180
+ def assert_preset_name_valid(value, keypath:)
181
+ unless %r(^[a-z_]*$) =~ value
182
+ @interface.fatal!("Invalid preset name: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
183
+ end
184
+ end
185
+
186
+ def assert_variable_name_valid(value, keypath:)
187
+ unless %r(^[A-Z_][A-Z0-9_]*$) =~ value
188
+ @interface.fatal!("Invalid variable name: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
189
+ end
190
+ end
191
+
192
+ def assert_variable_value_valid(value, keypath:)
193
+ if value.is_a?(String)
194
+ unless %r(^((?:[^\$]*(?:(?:\\\$)|(?<!\\)\$\((?:ENV:)?[A-Z_][A-Z0-9_]*\))*)*)$) =~ value
195
+ @interface.fatal!("Invalid variable value: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
196
+ end
197
+ elsif value.is_a?(Array) || value.is_a?(Hash)
198
+ @interface.fatal!("Invalid variable value: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
199
+ else
200
+ unless value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Numeric) || value.is_a?(NilClass)
201
+ @interface.fatal!("Invalid variable value: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
202
+ end
203
+ end
204
+ end
205
+
206
+ def assert_step_exists(value, keypath:)
207
+ unless @registry.get_by_name(value)
208
+ @interface.fatal!("Unknown step: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
209
+ end
210
+ end
211
+
212
+ def assert_step_parameter_name_valid(value, expected:, keypath:)
213
+ unless expected.include?(value)
214
+ expected_names = expected.map { |name| "'#{name}'" }.join(", ")
215
+ @interface.fatal!("Unknown step parameter: '#{value}' at '#{Utilities::keypath_to_s(keypath)}'. Expected one of: [#{expected_names}].")
216
+ end
217
+ end
218
+
219
+ def assert_step_parameter_value_valid(value, keypath:)
220
+ if value.is_a?(String)
221
+ unless %r(^((?:[^\$]*(?:(?:\\\$)|(?<!\\)\$\((?:ENV:)?[A-Z_]+\))*)*)$) =~ value
222
+ @interface.fatal!("Invalid step parameter value: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
223
+ end
224
+ elsif value.is_a?(Array)
225
+ value.each_with_index do |single_value, index|
226
+ assert_step_parameter_value_valid(single_value, keypath: keypath + [index])
227
+ end
228
+ elsif value.is_a?(Hash)
229
+ value.each_pair do |key, single_value|
230
+ assert_step_parameter_value_valid(single_value, keypath: keypath + [key])
231
+ end
232
+ else
233
+ unless value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Numeric) || value.is_a?(NilClass)
234
+ @interface.fatal!("Invalid step parameter value: '#{value}' at: '#{Utilities::keypath_to_s(keypath)}'.")
235
+ end
236
+ end
237
+ end
238
+
239
+ def assert_step_parameter_exists(value, expected:, keypath:)
240
+ unless value.include?(expected)
241
+ @interface.fatal!("Missing value for required step parameter: '#{expected}' at: '#{Utilities::keypath_to_s(keypath)}'.")
242
+ end
243
+ end
244
+
245
+ end
246
+
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,95 @@
1
+ #
2
+ # root.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "highway/compiler/analyze/tree/segments/text"
7
+ require "highway/compiler/analyze/tree/segments/variable"
8
+ require "highway/compiler/analyze/tree/stage"
9
+ require "highway/compiler/analyze/tree/step"
10
+ require "highway/compiler/analyze/tree/values/array"
11
+ require "highway/compiler/analyze/tree/values/hash"
12
+ require "highway/compiler/analyze/tree/values/primitive"
13
+ require "highway/compiler/analyze/tree/variable"
14
+
15
+ module Highway
16
+ module Compiler
17
+ module Analyze
18
+ module Tree
19
+
20
+ # This class represents a root node of a semantic tree. It contains
21
+ # other nodes, such as variables and steps.
22
+ class Root
23
+
24
+ public
25
+
26
+ # Initialize an instance.
27
+ def initialize()
28
+ @variables = Array.new()
29
+ @steps = Array.new()
30
+ @stages = Array.new()
31
+ end
32
+
33
+ # Name of the default preset.
34
+ #
35
+ # @return [String]
36
+ attr_accessor :default_preset
37
+
38
+ # Variables in the tree.
39
+ #
40
+ # @return [Array<Highway::Compiler::Analyze::Tree::Variable>]
41
+ attr_reader :variables
42
+
43
+ # Steps in the tree.
44
+ #
45
+ # @return [Array<Highway::Compiler::Analyze::Tree::Step>]
46
+ attr_reader :steps
47
+
48
+ # Stages in the tree.
49
+ #
50
+ # @return [Array<Highway::Compiler::Analyze::Tree::Stage>]
51
+ attr_reader :stages
52
+
53
+ # Add a variable to the tree.
54
+ #
55
+ # @param name [String] Name of the variable.
56
+ # @param value [Highway::Compiler::Analyze::Tree::Values::*] Value of the variable.
57
+ # @param preset [String] Parent preset of the variable.
58
+ #
59
+ # @return [Void]
60
+ def add_variable(name:, value:, preset:)
61
+ @variables << Variable.new(name: name, value: value, preset: preset)
62
+ end
63
+
64
+ # Add a step to the tree.
65
+ #
66
+ # @param index [Integer] Index of step in its scope.
67
+ # @param name [String] Name of the step.
68
+ # @param step_class [Class] Definition class of the step.
69
+ # @param parameters [Highway::Compiler::Analyze::Tree::Values::Hash] The hash value of step parameters.
70
+ # @param preset [String] Parent preset of the step.
71
+ # @param stage [String] Parent stage of the step.
72
+ #
73
+ # @return [Void]
74
+ def add_step(index:, name:, step_class:, parameters:, preset:, stage:)
75
+ @steps << Step.new(index: index, name: name, step_class: step_class, parameters: parameters, preset: preset, stage: stage)
76
+ end
77
+
78
+
79
+ # Add a stage to the tree.
80
+ #
81
+ # @param index [Integer] Index of the stage.
82
+ # @param name [String] Name of the stage.
83
+ # @param policy [Symbol] Execution policy of the stage.
84
+ #
85
+ # @return [Void]
86
+ def add_stage(index:, name:, policy:)
87
+ @stages << Stage.new(index: index, name: name, policy: policy)
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,36 @@
1
+ #
2
+ # text.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ module Highway
7
+ module Compiler
8
+ module Analyze
9
+ module Tree
10
+ module Segments
11
+
12
+ # This class represents a text value segment in the semantic tree. It
13
+ # consists of a raw text value.
14
+ class Text
15
+
16
+ public
17
+
18
+ # Initialize an instance.
19
+ #
20
+ # @param value [String] The raw text value.
21
+ def initialize(value)
22
+ @value = value
23
+ end
24
+
25
+ # The raw text value.
26
+ #
27
+ # @return [String]
28
+ attr_reader :value
29
+
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ #
2
+ # variable.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ module Highway
7
+ module Compiler
8
+ module Analyze
9
+ module Tree
10
+ module Segments
11
+
12
+ # This class represents a variable value segment in the semantic
13
+ # tree. It consists of a variable name and its lookup scope.
14
+ class Variable
15
+
16
+ public
17
+
18
+ # Initialize an instance.
19
+ #
20
+ # @param name [String] The variable name.
21
+ # @param scope [Symbol] The lookup scope of variable.
22
+ def initialize(name, scope:)
23
+ @name = name
24
+ @scope = scope
25
+ end
26
+
27
+ # The variable name.
28
+ #
29
+ # @return [String]
30
+ attr_reader :name
31
+
32
+ # The lookup scope of the variable.
33
+ #
34
+ # @return [Symbol]
35
+ attr_reader :scope
36
+
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end