highway 0.0.1 → 1.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.
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