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.
- checksums.yaml +7 -0
- data/lib/highway.rb +8 -4
- data/lib/highway/compiler/analyze/analyzer.rb +249 -0
- data/lib/highway/compiler/analyze/tree/root.rb +95 -0
- data/lib/highway/compiler/analyze/tree/segments/text.rb +36 -0
- data/lib/highway/compiler/analyze/tree/segments/variable.rb +43 -0
- data/lib/highway/compiler/analyze/tree/stage.rb +48 -0
- data/lib/highway/compiler/analyze/tree/step.rb +69 -0
- data/lib/highway/compiler/analyze/tree/values/array.rb +45 -0
- data/lib/highway/compiler/analyze/tree/values/base.rb +67 -0
- data/lib/highway/compiler/analyze/tree/values/hash.rb +45 -0
- data/lib/highway/compiler/analyze/tree/values/primitive.rb +43 -0
- data/lib/highway/compiler/analyze/tree/variable.rb +48 -0
- data/lib/highway/compiler/build/builder.rb +154 -0
- data/lib/highway/compiler/build/output/invocation.rb +70 -0
- data/lib/highway/compiler/build/output/manifest.rb +52 -0
- data/lib/highway/compiler/parse/parser.rb +92 -0
- data/lib/highway/compiler/parse/tree/root.rb +73 -0
- data/lib/highway/compiler/parse/tree/step.rb +62 -0
- data/lib/highway/compiler/parse/tree/variable.rb +48 -0
- data/lib/highway/compiler/parse/versions/v1.rb +110 -0
- data/lib/highway/compiler/suite.rb +56 -0
- data/lib/highway/environment.rb +282 -0
- data/lib/highway/fastlane/action.rb +67 -0
- data/lib/highway/interface.rb +135 -0
- data/lib/highway/main.rb +173 -0
- data/lib/highway/runtime/context.rb +229 -0
- data/lib/highway/runtime/report.rb +80 -0
- data/lib/highway/runtime/runner.rb +286 -0
- data/lib/highway/steps/infrastructure.rb +20 -0
- data/lib/highway/steps/library/action.rb +42 -0
- data/lib/highway/steps/library/appcenter.rb +106 -0
- data/lib/highway/steps/library/appstore.rb +137 -0
- data/lib/highway/steps/library/carthage.rb +67 -0
- data/lib/highway/steps/library/cocoapods.rb +76 -0
- data/lib/highway/steps/library/copy_artifacts.rb +36 -0
- data/lib/highway/steps/library/lane.rb +42 -0
- data/lib/highway/steps/library/sh.rb +36 -0
- data/lib/highway/steps/library/slack.rb +381 -0
- data/lib/highway/steps/library/testflight.rb +105 -0
- data/lib/highway/steps/library/xcode_archive.rb +162 -0
- data/lib/highway/steps/library/xcode_test.rb +264 -0
- data/lib/highway/steps/parameters/base.rb +52 -0
- data/lib/highway/steps/parameters/compound.rb +141 -0
- data/lib/highway/steps/parameters/single.rb +74 -0
- data/lib/highway/steps/registry.rb +92 -0
- data/lib/highway/steps/step.rb +52 -0
- data/lib/highway/steps/types/any.rb +65 -0
- data/lib/highway/steps/types/anyof.rb +64 -0
- data/lib/highway/steps/types/array.rb +44 -0
- data/lib/highway/steps/types/bool.rb +36 -0
- data/lib/highway/steps/types/enum.rb +44 -0
- data/lib/highway/steps/types/hash.rb +45 -0
- data/lib/highway/steps/types/number.rb +38 -0
- data/lib/highway/steps/types/set.rb +39 -0
- data/lib/highway/steps/types/string.rb +47 -0
- data/lib/highway/steps/types/url.rb +35 -0
- data/lib/highway/utilities.rb +51 -0
- data/lib/highway/version.rb +9 -1
- metadata +194 -22
- data/.gitignore +0 -4
- data/Gemfile +0 -4
- data/Rakefile +0 -1
- data/highway.gemspec +0 -24
data/lib/highway/main.rb
ADDED
@@ -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
|