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
@@ -0,0 +1,286 @@
1
+ #
2
+ # runner.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "fastlane"
7
+ require "fileutils"
8
+
9
+ require "highway/compiler/analyze/tree/root"
10
+ require "highway/environment"
11
+ require "highway/runtime/context"
12
+ require "highway/runtime/report"
13
+ require "highway/steps/infrastructure"
14
+ require "highway/utilities"
15
+
16
+ module Highway
17
+ module Runtime
18
+
19
+ # This class is responsible for evaluating invocation parameters, then
20
+ # validating and running step invocations.
21
+ class Runner
22
+
23
+ public
24
+
25
+ # Initialize an instance.
26
+ #
27
+ # @parameter context [Highway::Runtime::Context] The runtime context.
28
+ # @parameter manifest [Highway::Compiler::Build::Output::Manifest] The build manifest.
29
+ # @parameter manifest [Highway::Interface] The interface.
30
+ def initialize(manifest:, context:, interface:)
31
+ @manifest = manifest
32
+ @context = context
33
+ @interface = interface
34
+ end
35
+
36
+ # Run the build manifest.
37
+ #
38
+ # The runner prevalidates the step invocations if they don't contain any
39
+ # environments variables, then runs step invocations.
40
+ #
41
+ # This method catches exceptions thrown inside steps in order to maintain
42
+ # control over the execution policy.
43
+ def run()
44
+
45
+ # Validate invocations before running them. At this point we're able
46
+ # to evaluate non-environment variables, typecheck and validate the
47
+ # parameters.
48
+
49
+ prevalidate_invocations()
50
+
51
+ # Prepare the artifacts directory. If it doesn't exist, it's created at
52
+ # this point. If it exist, it's removed and re-created.
53
+
54
+ prepare_artifacts_dir()
55
+
56
+ # Run invocations one-by-one.
57
+
58
+ run_invocations()
59
+
60
+ # Print the metrics table containing the status of invocation, its step
61
+ # name and a duration it took.
62
+
63
+ report_metrics()
64
+
65
+ # Now it's time to raise an error if any of the steps failed.
66
+
67
+ if @context.reports_any_failed?
68
+ @interface.fatal!(nil)
69
+ end
70
+
71
+ end
72
+
73
+ private
74
+
75
+ def prevalidate_invocations()
76
+
77
+ @interface.header_success("Validating step parameters...")
78
+
79
+ @manifest.invocations.each { |invocation|
80
+ invocation.step_class.root_parameter.typecheck_and_prevalidate(
81
+ evaluate_parameter_for_prevalidation(invocation.parameters),
82
+ interface: @interface,
83
+ keypath: invocation.keypath,
84
+ )
85
+ }
86
+
87
+ @interface.success("All step parameters passed initial validation.")
88
+
89
+ end
90
+
91
+ def prepare_artifacts_dir()
92
+ FileUtils.remove_entry(@context.artifacts_dir) if File.exist?(@context.artifacts_dir)
93
+ FileUtils.mkdir(@context.artifacts_dir)
94
+ end
95
+
96
+ def run_invocations()
97
+ @manifest.invocations.each do |invocation|
98
+ report = run_invocation(invocation: invocation)
99
+ @context.add_report(report)
100
+ end
101
+ end
102
+
103
+ def run_invocation(invocation:)
104
+
105
+ # Prepare a report instance and some temporary variables to be used in
106
+ # metrics.
107
+
108
+ report = Report.new(
109
+ invocation: invocation,
110
+ artifacts_dir: @context.artifacts_dir,
111
+ )
112
+
113
+ step_name = invocation.step_class.name
114
+ time_started = Time.now
115
+
116
+ if !@context.reports_any_failed? || invocation.policy == :always
117
+
118
+ # Only run the step if no previous step has failed or if the step
119
+ # should always be executed.
120
+
121
+ @interface.header_success("Running step: #{step_name}...")
122
+
123
+ begin
124
+
125
+ # Evaluate, typecheck and map the invocation parameters. At this
126
+ # point we're able to evaluate environment variables.
127
+
128
+ evaluated_parameters = Utilities::hash_map(invocation.parameters.children) { |name, value|
129
+ [name, evaluate_parameter(value)]
130
+ }
131
+
132
+ parameters = invocation.step_class.root_parameter.typecheck_and_validate(
133
+ evaluated_parameters,
134
+ interface: @interface,
135
+ keypath: invocation.keypath,
136
+ )
137
+
138
+ # Run the step invocation. This is where steps are executed.
139
+
140
+ invocation.step_class.run(
141
+ parameters: parameters,
142
+ context: @context,
143
+ report: report,
144
+ )
145
+
146
+ report.result = :success
147
+
148
+ rescue FastlaneCore::Interface::FastlaneError, FastlaneCore::Interface::FastlaneCommonException => error
149
+
150
+ # These two errors should not count as crashes and should not print
151
+ # backtrace unless running in verbose mode. This follows the
152
+ # behavior of `Fastlane::Runner`.
153
+
154
+ @interface.error(error.message)
155
+ @interface.error(error.backtrace.join("\n")) if @context.env.verbose?
156
+
157
+ report.result = :failure
158
+ report.error = error
159
+
160
+ rescue FastlaneCore::Interface::FastlaneShellError => error
161
+
162
+ # This error should be treated in a special way as its message
163
+ # contains the whole command output. It should only be printed in
164
+ # verbose mode.
165
+
166
+ @interface.error(error.message.split("\n").first)
167
+ @interface.error(error.message.split("\n").drop(1).join("\n")) if @context.env.verbose?
168
+ @interface.error(error.backtrace.join("\n")) if @context.env.verbose?
169
+
170
+ report.result = :failure
171
+ report.error = error
172
+
173
+ rescue => error
174
+
175
+ # Chances are that this is `FastlaneCore::Interface::FastlaneCrash`
176
+ # but it could be another error as well. For these error a backtrace
177
+ # should always be printed. This follows the behavior of
178
+ # `Fastlane::Runner`.
179
+
180
+ @interface.error("#{error.class}: #{error.message}")
181
+ @interface.error(error.backtrace.join("\n"))
182
+
183
+ report.result = :failure
184
+ report.error = error
185
+
186
+ end
187
+
188
+ else
189
+
190
+ @interface.header_warning("Skipping step: #{step_name}...")
191
+ @interface.warning("Skipping because a previous step has failed.")
192
+
193
+ report.result = :skip
194
+
195
+ end
196
+
197
+ report.duration = (Time.now - time_started).round
198
+
199
+ report
200
+
201
+ end
202
+
203
+ def evaluate_parameter(value)
204
+ if value.is_a?(Compiler::Analyze::Tree::Values::Primitive)
205
+ value.segments.reduce("") { |memo, segment|
206
+ if segment.is_a?(Compiler::Analyze::Tree::Segments::Text)
207
+ memo + segment.value
208
+ elsif segment.is_a?(Compiler::Analyze::Tree::Segments::Variable) && segment.scope == :env
209
+ if @context.env.include?(segment.name)
210
+ memo + @context.env.find(segment.name) || ""
211
+ else
212
+ @interface.fatal!("The value for key '#{segment.name}' does not exist.")
213
+ end
214
+ end
215
+ }
216
+ elsif value.is_a?(Compiler::Analyze::Tree::Values::Array)
217
+ value.children.map { |value|
218
+ evaluate_parameter(value)
219
+ }
220
+ elsif value.is_a?(Compiler::Analyze::Tree::Values::Hash)
221
+ Utilities::hash_map(value.children) { |key, value|
222
+ [key, evaluate_parameter(value)]
223
+ }
224
+ end
225
+ end
226
+
227
+ def evaluate_parameter_for_prevalidation(value)
228
+ if value.is_a?(Compiler::Analyze::Tree::Values::Primitive)
229
+ if value.select_variable_segments_with_scope(:env).empty?
230
+ evaluate_parameter(value)
231
+ else
232
+ :ignore
233
+ end
234
+ elsif value.is_a?(Compiler::Analyze::Tree::Values::Array)
235
+ value.children.map { |value|
236
+ evaluate_parameter_for_prevalidation(value)
237
+ }
238
+ elsif value.is_a?(Compiler::Analyze::Tree::Values::Hash)
239
+ Utilities::hash_map(value.children) { |key, value|
240
+ [key, evaluate_parameter_for_prevalidation(value)]
241
+ }
242
+ end
243
+ end
244
+
245
+ def report_metrics()
246
+
247
+ rows = @context.reports.each.map { |report|
248
+
249
+ status = case report.result
250
+ when :success then report.invocation.index.to_s
251
+ when :failure then "x"
252
+ when :skip then "-"
253
+ end
254
+
255
+ name = report.invocation.step_class.name
256
+
257
+ minutes = (report.duration / 60).floor
258
+ seconds = report.duration % 60
259
+
260
+ duration = "#{minutes}m #{seconds}s" if minutes > 0
261
+ duration ||= "#{seconds}s"
262
+
263
+ row = [status, name, duration].map { |text|
264
+ case report.result
265
+ when :success then text.green
266
+ when :failure then text.red.bold
267
+ when :skip then text.yellow
268
+ end
269
+ }
270
+
271
+ row
272
+
273
+ }
274
+
275
+ @interface.table(
276
+ title: "Highway Summary".yellow,
277
+ headings: ["", "Step", "Duration"],
278
+ rows: rows
279
+ )
280
+
281
+ end
282
+
283
+ end
284
+
285
+ end
286
+ end
@@ -0,0 +1,20 @@
1
+ #
2
+ # infrastructure.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "fastlane"
7
+
8
+ require "highway/steps/parameters/compound"
9
+ require "highway/steps/parameters/single"
10
+ require "highway/steps/step"
11
+ require "highway/steps/types/any"
12
+ require "highway/steps/types/anyof"
13
+ require "highway/steps/types/array"
14
+ require "highway/steps/types/bool"
15
+ require "highway/steps/types/enum"
16
+ require "highway/steps/types/hash"
17
+ require "highway/steps/types/number"
18
+ require "highway/steps/types/set"
19
+ require "highway/steps/types/string"
20
+ require "highway/steps/types/url"
@@ -0,0 +1,42 @@
1
+ #
2
+ # action.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "highway/steps/infrastructure"
7
+
8
+ module Highway
9
+ module Steps
10
+ module Library
11
+
12
+ class ActionStep < Step
13
+
14
+ def self.name
15
+ "action"
16
+ end
17
+
18
+ def self.parameters
19
+ [
20
+ Parameters::Single.new(
21
+ name: "name",
22
+ required: true,
23
+ type: Types::String.new(),
24
+ ),
25
+ Parameters::Single.new(
26
+ name: "options",
27
+ required: false,
28
+ type: Types::Hash.new(Types::Any.new()),
29
+ default: {},
30
+ ),
31
+ ]
32
+ end
33
+
34
+ def self.run(parameters:, context:, report:)
35
+ context.run_action(parameters["name"], options: parameters["options"])
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,106 @@
1
+ #
2
+ # appcenter.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "fastlane"
7
+
8
+ require "highway/steps/infrastructure"
9
+
10
+ module Highway
11
+ module Steps
12
+ module Library
13
+
14
+ class AppCenterStep < Step
15
+
16
+ def self.name
17
+ "appcenter"
18
+ end
19
+
20
+ def self.plugin_name
21
+ "fastlane-plugin-appcenter"
22
+ end
23
+
24
+ def self.parameters
25
+ [
26
+ Parameters::Single.new(
27
+ name: "app_name",
28
+ required: true,
29
+ type: Types::String.new()
30
+ ),
31
+ Parameters::Single.new(
32
+ name: "api_token",
33
+ required: true,
34
+ type: Types::String.new()
35
+ ),
36
+ Parameters::Single.new(
37
+ name: "distribution_group",
38
+ required: false,
39
+ type: Types::String.new(),
40
+ default: "*"
41
+ ),
42
+ Parameters::Single.new(
43
+ name: "dsym",
44
+ required: false,
45
+ type: Types::String.new()
46
+ ),
47
+ Parameters::Single.new(
48
+ name: "notify",
49
+ required: false,
50
+ type: Types::Bool.new(),
51
+ default: false
52
+ ),
53
+ Parameters::Single.new(
54
+ name: "owner_name",
55
+ required: true,
56
+ type: Types::String.new()
57
+ )
58
+ ]
59
+ end
60
+
61
+ def self.run(parameters:, context:, report:)
62
+
63
+ app_name = parameters["app_name"]
64
+ api_token = parameters["api_token"]
65
+ destinations = parameters["distribution_group"]
66
+ dsym = parameters["dsym"]
67
+ notify = parameters["notify"]
68
+ owner_name = parameters["owner_name"]
69
+
70
+ context.assert_gem_available!(plugin_name)
71
+
72
+ context.run_action("appcenter_upload", options: {
73
+ api_token: api_token,
74
+ owner_name: owner_name,
75
+ app_name: app_name,
76
+ destinations: destinations,
77
+ notify_testers: notify,
78
+ dsym: dsym
79
+ })
80
+
81
+ response = context.fastlane_lane_context[:APPCENTER_BUILD_INFORMATION]
82
+
83
+ report[:deployment] = {
84
+
85
+ service: "AppCenter",
86
+
87
+ package: {
88
+ name: response["app_display_name"],
89
+ version: response["short_version"],
90
+ build: response["version"],
91
+ },
92
+
93
+ urls: {
94
+ install: response["install_url"],
95
+ view: response["download_url"],
96
+ },
97
+
98
+ }
99
+
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+ end
106
+ end