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
@@ -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
|