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,173 @@
1
+ #
2
+ # main.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "fastlane"
7
+
8
+ require "highway/compiler/suite"
9
+ require "highway/environment"
10
+ require "highway/interface"
11
+ require "highway/runtime/context"
12
+ require "highway/runtime/runner"
13
+ require "highway/steps/registry"
14
+
15
+ module Highway
16
+
17
+ # This class is a main entry point to Highway.
18
+ class Main
19
+
20
+ public
21
+
22
+ # Initialize an instance.
23
+ #
24
+ # @param entrypoint [Symbol] The entry point.
25
+ # @param path [String] Path to the configuration file.
26
+ # @param preset [String] Preset to run.
27
+ # @param fastlane_runner [Fastlane::Runner] The fastlane runner.
28
+ # @param fastlane_lane_context [Hash] The fastlane lane context.
29
+ def initialize(entrypoint:, path:, preset:, fastlane_runner:, fastlane_lane_context:)
30
+ @entrypoint = entrypoint
31
+ @path = path
32
+ @preset = preset
33
+ @fastlane_runner = fastlane_runner
34
+ @fastlane_lane_context = fastlane_lane_context
35
+ end
36
+
37
+ # Run Highway.
38
+ #
39
+ # @return [Void]
40
+ def run()
41
+
42
+ # Always run Highway in the root directory of the project. This should
43
+ # standardize the legacy directory behavior described here:
44
+ # https://docs.fastlane.tools/advanced/fastlane/#directory-behavior.
45
+
46
+ Dir.chdir(running_dir) do
47
+
48
+ # Print the header, similar to Fastlane's "driving the lane".
49
+
50
+ interface.success("Driving the Highway preset '#{@preset}' 🏎")
51
+
52
+ # Construct a steps registry and load steps from the default library
53
+ # by requiring files in `highway/steps/library` and registering all
54
+ # classes that inherit from `Highway::Steps::Step`.
55
+
56
+ registry = Steps::Registry.new_and_load_default_library()
57
+
58
+ # Set up the compiler and compile Highway configuration file into the
59
+ # build manifest. See `highway/compiler/parse`, `highway/compiler/analyze`
60
+ # and `highway/compiler/build` for more information.
61
+
62
+ compiler = Compiler::Suite.new(
63
+ registry: registry,
64
+ interface: interface,
65
+ )
66
+
67
+ manifest = compiler.compile(
68
+ path: File.expand_path(@path, running_dir),
69
+ preset: @preset,
70
+ )
71
+
72
+ # At this point the compilation is done. Now, construct the runtime
73
+ # context, set up the runner and run the compiled build manifest.
74
+
75
+ context = Runtime::Context.new(
76
+ fastlane_runner: @fastlane_runner,
77
+ fastlane_lane_context: @fastlane_lane_context,
78
+ env: env,
79
+ interface: interface,
80
+ )
81
+
82
+ runner = Runtime::Runner.new(
83
+ manifest: manifest,
84
+ context: context,
85
+ interface: interface,
86
+ )
87
+
88
+ runner.run()
89
+
90
+ # We can safely print the success summary message because fatal errors
91
+ # will be catched by the rescue block below.
92
+
93
+ @interface.whitespace()
94
+ @interface.success("Wubba lubba dub dub, Highway preset '#{@preset}' has succeeded!")
95
+
96
+ end
97
+
98
+ rescue StandardError => error
99
+
100
+ # Unless the error contains any message we should print it right now but
101
+ # as an error so that we can still control the output.
102
+
103
+ interface.error(error) unless error.message.empty?
104
+
105
+ # We should take care of the unnecessary printing of Fastlane lane
106
+ # context but only if we're running from lane entry point (otherwise we
107
+ # could affect other actions and fallback lanes user has set up).
108
+ #
109
+ # So, if we're running from lane entry point, we should clear the lane
110
+ # context before raising a fatal error. If we're in verbose mode, we
111
+ # should additionally print it before cleaning.
112
+
113
+ if @entrypoint == :lane
114
+ report_fastlane_lane_context() if env.verbose?
115
+ clear_fastlane_lane_context()
116
+ end
117
+
118
+ # Now we throw a fatal error that tells Fastlane to abort.
119
+
120
+ interface.whitespace()
121
+ interface.fatal!("Highway preset '#{@preset}' has failed with one or more errors. Please examine the above log.")
122
+
123
+ end
124
+
125
+ private
126
+
127
+ # The environment instance.
128
+ #
129
+ # @return [Highway::Environment]
130
+ def env
131
+ @env ||= Environment.new()
132
+ end
133
+
134
+ # The interface instance.
135
+ #
136
+ # @return [Highway::Interface]
137
+ def interface
138
+ @interface ||= Interface.new()
139
+ end
140
+
141
+ # The running directory to be used.
142
+ #
143
+ # @return [Path]
144
+ def running_dir
145
+ File.expand_path(File.join(FastlaneCore::FastlaneFolder.path, ".."))
146
+ end
147
+
148
+ # Report Fastlane lane context as a table.
149
+ #
150
+ # @return [Void]
151
+ def report_fastlane_lane_context()
152
+
153
+ lane_context_rows = @fastlane_lane_context.collect do |key, content|
154
+ [key, content.to_s]
155
+ end
156
+
157
+ interface.table(
158
+ title: "Fastlane Context".yellow,
159
+ rows: lane_context_rows
160
+ )
161
+
162
+ end
163
+
164
+ # Clear all values in Fastlane lane context.
165
+ #
166
+ # @return [Void]
167
+ def clear_fastlane_lane_context()
168
+ @fastlane_lane_context.clear()
169
+ end
170
+
171
+ end
172
+
173
+ end
@@ -0,0 +1,229 @@
1
+ #
2
+ # context.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ require "fastlane"
7
+
8
+ require "highway/utilities"
9
+
10
+ module Highway
11
+ module Runtime
12
+
13
+ # This class is responsible for maintaining runtime context between step
14
+ # invocations, allowing steps to access runtimes of both Fastlane and
15
+ # Highway.
16
+ class Context
17
+
18
+ public
19
+
20
+ # Initialize an instance.
21
+ #
22
+ # @param fastlane_runner [Fastlane::Runner] The Fastlane runner.
23
+ # @param fastlane_lane_context [Hash] The Fastlane lane context.
24
+ # @param env [Highway::Runtime::Environment] The runtime environment.
25
+ # @param reporter [Highway::Interface] The interface.
26
+ def initialize(fastlane_runner:, fastlane_lane_context:, env:, interface:)
27
+ @fastlane_runner = fastlane_runner
28
+ @fastlane_lane_context = fastlane_lane_context
29
+ @env = env
30
+ @interface = interface
31
+ @reports = Array.new()
32
+ end
33
+
34
+ # @!group Context and environment
35
+
36
+ # The environment of the runtime context.
37
+ #
38
+ # @return [Highway::Runtime::Environment]
39
+ attr_reader :env
40
+
41
+ # The Fastlane lane context.
42
+ #
43
+ # @return [Hash]
44
+ attr_reader :fastlane_lane_context
45
+
46
+ # The interface.
47
+ #
48
+ # @return [Highway::Interface]
49
+ attr_reader :interface
50
+
51
+ # The path to directory containing artifacts.
52
+ #
53
+ # @return [String]
54
+ def artifacts_dir
55
+ File.join(File.expand_path(FastlaneCore::FastlaneFolder.path), "highway")
56
+ end
57
+
58
+ # Execute the given block in the scope of overridden ENV variables. After
59
+ # that, old values will be restored.
60
+ #
61
+ # @param new_env [Hash] ENV variables to override.
62
+ # @param &block [Proc] A block to execute.
63
+ def with_modified_env(new_env, &block)
64
+
65
+ old_env = Utilities::hash_map(new_env.keys) { |name|
66
+ [name, ENV[name]]
67
+ }
68
+
69
+ new_env.each_pair { |name, value|
70
+ ENV[name] = value
71
+ }
72
+
73
+ block.call()
74
+
75
+ old_env.each_pair { |name, value|
76
+ ENV[name] = value
77
+ }
78
+
79
+ end
80
+
81
+ # @!group Reports
82
+
83
+ # All reports in the runtime context.
84
+ #
85
+ # @return [Array<Highway::Runtime::Report>]
86
+ attr_reader :reports
87
+
88
+ # Add a runtime report to the context.
89
+ #
90
+ # @param report [Highway::Runtime::Report] The report.
91
+ #
92
+ # @return [Void]
93
+ def add_report(report)
94
+ @reports << report
95
+ end
96
+
97
+ # Whether any of the previous reports failed.
98
+ #
99
+ # @return [Boolean]
100
+ def reports_any_failed?
101
+ @reports.any? { |report| report.result == :failure }
102
+ end
103
+
104
+ # Total duration of all previous reports.
105
+ #
106
+ # @return [Numeric]
107
+ def duration_so_far
108
+ @reports.reduce(0) { |memo, report| memo + report.duration }
109
+ end
110
+
111
+ # Reports containing information about tests.
112
+ #
113
+ # @return [Array<Highway::Runtime::Report>]
114
+ def test_reports
115
+ @reports.select { |report| report[:test] != nil }
116
+ end
117
+
118
+ # Reports containing information about archives.
119
+ #
120
+ # @return [Array<Highway::Runtime::Report>]
121
+ def archive_reports
122
+ @reports.select { |report| report[:archive] != nil }
123
+ end
124
+
125
+ # Reports containing information about deployments.
126
+ #
127
+ # @return [Array<Highway::Runtime::Report>]
128
+ def deployment_reports
129
+ @reports.select { |report| report[:deployment] != nil }
130
+ end
131
+
132
+ # @!group Assertions
133
+
134
+ # Assert that a gem with specified name is available.
135
+ #
136
+ # @param name [String] Name of the gem.
137
+ def assert_gem_available!(name)
138
+ Fastlane::Actions.verify_gem!(name)
139
+ end
140
+
141
+ # Assert that an executable with specified name is available.
142
+ #
143
+ # @param name [String] Name of executable.
144
+ def assert_executable_available!(name)
145
+ unless FastlaneCore::CommandExecutor.which(name) != nil
146
+ @interface.fatal!("Required executable '#{name}' could not be found. Make sure it's installed.")
147
+ end
148
+ end
149
+
150
+ # @!group Interfacing with Fastlane
151
+
152
+ # Run a Fastlane lane.
153
+ #
154
+ # @param name [String, Symbol] Name of the lane.
155
+ # @param options [Hash] Options passed to the lane.
156
+ def run_lane(name, options:)
157
+
158
+ unless contains_lane?(name)
159
+ @interface.fatal!("Can't execute lane '#{name}' because it doesn't exist.")
160
+ end
161
+
162
+ unless !contains_action?(name)
163
+ @interface.fatal!("Can't execute lane '#{name}' because an action with the same name exists.")
164
+ end
165
+
166
+ run_lane_or_action(name, options)
167
+
168
+ end
169
+
170
+ # Run a Fastlane action.
171
+ #
172
+ # @param name [String, Symbol] Name of the action.
173
+ # @param options [Hash] Options passed to the action.
174
+ def run_action(name, options:)
175
+
176
+ unless contains_action?(name)
177
+ @interface.fatal!("Can't execute action '#{name}' because it doesn't exist.")
178
+ end
179
+
180
+ unless !contains_lane?(name)
181
+ @interface.fatal!("Can't execute action '#{name}' because a lane with the same name exists.")
182
+ end
183
+
184
+ run_lane_or_action(name, options)
185
+
186
+ end
187
+
188
+ # @!group Interfacing with shell
189
+
190
+ # Whether `bundle exec` is available and should be used if possible.
191
+ #
192
+ # @return [Boolean]
193
+ def should_use_bundle_exec?
194
+ return File.exist?("Gemfile")
195
+ end
196
+
197
+ # Run a shell command.
198
+ #
199
+ # @param command [String, Array] A shell command.
200
+ # @param bundle_exec [Boolean] Whether to use `bundle exec` if possible.
201
+ # @param silent [Boolean] Whether to run the command silently.
202
+ # @param on_error [Proc] Called if command exits with a non-zero code.
203
+ def run_sh(command, bundle_exec: false, silent: false, rescue_error: nil)
204
+ command = ["bundle exec", command].flatten if bundle_exec && should_use_bundle_exec?
205
+ Fastlane::Actions.sh(command, log: !silent, error_callback: rescue_error)
206
+ end
207
+
208
+ private
209
+
210
+ def contains_lane?(lane_name)
211
+ lane = @fastlane_runner.lanes.fetch(nil, {}).fetch(lane_name.to_sym, nil)
212
+ lane != nil
213
+ end
214
+
215
+ def contains_action?(action_name)
216
+ action = @fastlane_runner.class_reference_from_action_name(action_name.to_sym)
217
+ action ||= @fastlane_runner.class_reference_from_action_alias(action_name.to_sym)
218
+ action != nil
219
+ end
220
+
221
+ def run_lane_or_action(name, args)
222
+ symbolicated_args = Utilities::hash_map(args || {}) { |key, value| [key.to_sym, value] }
223
+ @fastlane_runner.trigger_action_by_name(name.to_sym, Dir.pwd, true, *[symbolicated_args])
224
+ end
225
+
226
+ end
227
+
228
+ end
229
+ end
@@ -0,0 +1,80 @@
1
+ #
2
+ # report.rb
3
+ # Copyright © 2019 Netguru S.A. All rights reserved.
4
+ #
5
+
6
+ module Highway
7
+ module Runtime
8
+
9
+ # This class represents a report of running a step during runtime. It
10
+ # contains metrics (such as status and duration) as well as metadata set by
11
+ # steps themselves.
12
+ class Report
13
+
14
+ # Initialize an instance.
15
+ #
16
+ # @param invocation [Highway::Compiler::Build::Output::Invocation] The invocation.
17
+ # @param artifacts_dir [Path] Dir in which to prepare artifacts.
18
+ def initialize(invocation:, artifacts_dir:)
19
+ @invocation = invocation
20
+ @artifacts_dir = artifacts_dir
21
+ @data = Hash.new()
22
+ end
23
+
24
+ # The invocation.
25
+ #
26
+ # @return [Highway::Compiler::Build::Output::Invocation]
27
+ attr_accessor :invocation
28
+
29
+ # Result of the step, one of: `:success`, `:failure`, `:skip`.
30
+ #
31
+ # @return [Symbol]
32
+ attr_accessor :result
33
+
34
+ # An error that the step failed with, if any.
35
+ #
36
+ # @return [FastlaneCore::Interface::FastlaneException, nil]
37
+ attr_accessor :error
38
+
39
+ # Duration of the step, in seconds.
40
+ #
41
+ # @return [Numeric]
42
+ attr_accessor :duration
43
+
44
+ # The custom data in the report.
45
+ #
46
+ # @return [Hash]
47
+ attr_reader :data
48
+
49
+ # Get custom data value for given key.
50
+ #
51
+ # @param key [String] A key.
52
+ #
53
+ # @return [Object, nil]
54
+ def [](key)
55
+ @data[key]
56
+ end
57
+
58
+ # Set custom data value for given key.
59
+ #
60
+ # @param key [String] A key.
61
+ # @param value [Object, nil] A value.
62
+ #
63
+ # @return [Void]
64
+ def []=(key, value)
65
+ @data[key] = value
66
+ end
67
+
68
+ # Prepare an artifact file with a given name and return its path.
69
+ #
70
+ # @param name [String] An artifact file name.
71
+ #
72
+ # @return [String]
73
+ def prepare_artifact(name)
74
+ File.join(@artifacts_dir, "#{invocation.identifier}-#{name}")
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+ end