fastlane-plugin-flutter 0.2.7 → 0.3.0
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 +4 -4
- data/README.md +11 -9
- data/lib/fastlane/plugin/flutter/actions/flutter_action.rb +8 -291
- data/lib/fastlane/plugin/flutter/actions/flutter_bootstrap_action.rb +80 -0
- data/lib/fastlane/plugin/flutter/actions/flutter_build_action.rb +124 -0
- data/lib/fastlane/plugin/flutter/actions/flutter_generate_action.rb +68 -0
- data/lib/fastlane/plugin/flutter/base/flutter_action_base.rb +16 -0
- data/lib/fastlane/plugin/flutter/helper/flutter_bootstrap_helper.rb +19 -0
- data/lib/fastlane/plugin/flutter/helper/flutter_generate_build_runner_helper.rb +14 -0
- data/lib/fastlane/plugin/flutter/helper/flutter_generate_intl_helper.rb +110 -0
- data/lib/fastlane/plugin/flutter/helper/flutter_helper.rb +21 -133
- data/lib/fastlane/plugin/flutter/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e378c20ed4cdebc4af0d1034b9a33c1c6836ad8
|
4
|
+
data.tar.gz: 722fa1646960e5838262036a875206bb404543d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53db9cf6533fc97b1b5ae367764dffeddcb6bccca320e3fe70214e88c4b90f99fdd2834ccad6d8e7797fc6a02c9ecd4df292ee92f5e19987bfa925be7ac6f406
|
7
|
+
data.tar.gz: 5e0387e7c92ffe9d36ba25f26daa9dab60ab25eab9f9a0f0403fe1baa91b875c783b87fdbf86f6db1c91a3f570c8f819b92b3800b2d4edab779397810ac53491
|
data/README.md
CHANGED
@@ -6,8 +6,8 @@
|
|
6
6
|
|
7
7
|
This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-flutter`, add it to your project by running:
|
8
8
|
|
9
|
-
```
|
10
|
-
fastlane add_plugin flutter
|
9
|
+
```shell
|
10
|
+
$ fastlane add_plugin flutter
|
11
11
|
```
|
12
12
|
|
13
13
|
## About flutter
|
@@ -16,21 +16,23 @@ Flutter actions plugin for Fastlane.
|
|
16
16
|
|
17
17
|
## Example
|
18
18
|
|
19
|
-
Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin.
|
20
|
-
|
21
|
-
**Note to author:** Please set up a sample project to make it easy for users to explore what your plugin does. Provide everything that is necessary to try out the plugin in this project (including a sample Xcode/Android project if necessary)
|
19
|
+
Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin.
|
22
20
|
|
23
21
|
## Run tests for this plugin
|
24
22
|
|
25
23
|
To run both the tests, and code style validation, run
|
26
24
|
|
27
|
-
```
|
28
|
-
|
25
|
+
```shell
|
26
|
+
$ bundle install
|
27
|
+
$ bundle exec rake
|
28
|
+
$ bundle exec fastlane end_to_end_test
|
29
29
|
```
|
30
30
|
|
31
31
|
To automatically fix many of the styling issues, use
|
32
|
-
|
33
|
-
|
32
|
+
|
33
|
+
```shell
|
34
|
+
$ bundle install
|
35
|
+
$ bundle exec rubocop -a
|
34
36
|
```
|
35
37
|
|
36
38
|
## Issues and Feedback
|
@@ -1,313 +1,30 @@
|
|
1
1
|
require 'fastlane/action'
|
2
|
+
require_relative '../base/flutter_action_base'
|
2
3
|
require_relative '../helper/flutter_helper'
|
3
4
|
|
4
|
-
# This is the entry point for this plugin. For more information on writing
|
5
|
-
# plugins: https://docs.fastlane.tools/advanced/
|
6
|
-
|
7
5
|
module Fastlane
|
8
6
|
module Actions
|
9
|
-
module SharedValues
|
10
|
-
FLUTTER_OUTPUT_APP = :FLUTTER_OUTPUT_APP
|
11
|
-
FLUTTER_OUTPUT_APK = :FLUTTER_OUTPUT_APK
|
12
|
-
FLUTTER_OUTPUT_BUILD_NUMBER_OVERRIDE =
|
13
|
-
:FLUTTER_OUTPUT_BUILD_NUMBER_OVERRIDE
|
14
|
-
FLUTTER_OUTPUT_BUILD_NAME_OVERRIDE = :FLUTTER_OUTPUT_BUILD_NAME_OVERRIDE
|
15
|
-
end
|
16
|
-
|
17
7
|
class FlutterAction < Action
|
18
|
-
|
19
|
-
|
20
|
-
PLATFORM_TO_FLUTTER = {
|
21
|
-
ios: 'ios',
|
22
|
-
android: 'apk',
|
23
|
-
}
|
24
|
-
|
25
|
-
PLATFORM_TO_OUTPUT = {
|
26
|
-
ios: SharedValues::FLUTTER_OUTPUT_APP,
|
27
|
-
android: SharedValues::FLUTTER_OUTPUT_APK,
|
28
|
-
}
|
8
|
+
extend FlutterActionBase
|
29
9
|
|
30
10
|
def self.run(params)
|
31
|
-
|
32
|
-
#
|
33
|
-
# FastlaneCore::PrintTable.print_values(
|
34
|
-
# config: params,
|
35
|
-
# title: "Summary for Flutter plugin #{Fastlane::Flutter::VERSION}",
|
36
|
-
# )
|
37
|
-
|
38
|
-
case params[:action]
|
39
|
-
when 'build'
|
40
|
-
# A map of fastlane platform name into "flutter build" args list.
|
41
|
-
build_args = {}
|
42
|
-
|
43
|
-
if lane_context.key?(SharedValues::PLATFORM_NAME)
|
44
|
-
fastlane_platform = lane_context[SharedValues::PLATFORM_NAME]
|
45
|
-
build_args[fastlane_platform] =
|
46
|
-
[PLATFORM_TO_FLUTTER[fastlane_platform]]
|
47
|
-
else
|
48
|
-
# If platform is unspecified, build for all platforms.
|
49
|
-
PLATFORM_TO_FLUTTER.each_key do |platform|
|
50
|
-
build_args[platform] = [PLATFORM_TO_FLUTTER[platform]]
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
if params[:debug]
|
55
|
-
build_args.each_value do |a|
|
56
|
-
a.push('--debug')
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
unless params[:codesign]
|
61
|
-
build_args[:ios].push('--no-codesign') if build_args.key?(:ios)
|
62
|
-
end
|
63
|
-
|
64
|
-
if lane_context[SharedValues::FLUTTER_OUTPUT_BUILD_NUMBER_OVERRIDE] =
|
65
|
-
build_number = Helper::FlutterHelper.build_number(
|
66
|
-
params[:build_number_override]
|
67
|
-
)
|
68
|
-
build_args.each_value do |a|
|
69
|
-
a.push('--build-number', build_number.to_s)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
if lane_context[SharedValues::FLUTTER_OUTPUT_BUILD_NAME_OVERRIDE] =
|
74
|
-
build_name = Helper::FlutterHelper.build_name(
|
75
|
-
params[:build_name_override]
|
76
|
-
)
|
77
|
-
build_args.each_value do |a|
|
78
|
-
a.push('--build-name', build_name)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Note: this statement is expected to return a value (map of platform
|
83
|
-
# into file name).
|
84
|
-
Hash[build_args.keys.zip(build_args.map do |platform, args|
|
85
|
-
sh('flutter', 'build', *args) do |status, res|
|
86
|
-
if status.success?
|
87
|
-
# Dirty hacks ahead!
|
88
|
-
if PLATFORM_TO_OUTPUT.key?(platform)
|
89
|
-
if res =~ /^Built (.*?)(:? \([^)]*\))?\.$/
|
90
|
-
lane_context[PLATFORM_TO_OUTPUT[platform]] =
|
91
|
-
File.absolute_path($1)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
else
|
95
|
-
# fastlane does not fail automatically if we provide a block.
|
96
|
-
UI.build_failure!("flutter build #{platform} has failed.")
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end.compact)]
|
100
|
-
when 'test'
|
101
|
-
sh *%w(flutter test)
|
102
|
-
when 'analyze'
|
103
|
-
sh *%w(flutter analyze)
|
104
|
-
when 'format'
|
105
|
-
sh *%w(flutter format .)
|
106
|
-
when 'l10n'
|
107
|
-
run_l10n(params)
|
108
|
-
when 'bootstrap'
|
109
|
-
Helper::FlutterHelper.bootstrap_android(params[:android_licenses])
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def self.run_l10n(params)
|
114
|
-
unless params[:l10n_strings_file]
|
115
|
-
UI.user_error!('l10n_strings_file is a required parameter for ' \
|
116
|
-
'l10n action')
|
117
|
-
end
|
118
|
-
|
119
|
-
output_dir = 'lib/l10n'
|
120
|
-
l10n_messages_file = File.join(output_dir, 'intl_messages.arb')
|
121
|
-
# This file will not exist before it's generated for the first time.
|
122
|
-
if File.exist?(l10n_messages_file)
|
123
|
-
l10n_messages_was = File.read(l10n_messages_file)
|
124
|
-
end
|
125
|
-
|
126
|
-
extract_to_arb_options = ["--output-dir=#{output_dir}"]
|
127
|
-
if params[:l10n_strings_locale]
|
128
|
-
extract_to_arb_options.push(
|
129
|
-
"--locale=#{params[:l10n_strings_locale]}"
|
130
|
-
)
|
131
|
-
end
|
132
|
-
|
133
|
-
sh *%w(flutter packages pub run intl_translation:extract_to_arb),
|
134
|
-
*extract_to_arb_options, params[:l10n_strings_file]
|
135
|
-
|
136
|
-
if l10n_messages_was
|
137
|
-
# intl will update @@last_modified even if there are no updates;
|
138
|
-
# this leaves Git directory unnecessary dirty. If that's the only
|
139
|
-
# change, just restore the previous contents.
|
140
|
-
if Helper::FlutterHelper.restore_l10n_timestamp(
|
141
|
-
l10n_messages_file, l10n_messages_was
|
142
|
-
)
|
143
|
-
UI.message(
|
144
|
-
"@@last_modified has been restored in #{l10n_messages_file}"
|
145
|
-
)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
# Sort files for consistency, because messages_all.dart will have
|
150
|
-
# imports ordered as in the command line below.
|
151
|
-
arb_files = Dir.glob(File.join(output_dir, 'intl_*.arb')).sort
|
152
|
-
|
153
|
-
if params[:l10n_verify_arb]
|
154
|
-
errors_found = arb_files.any? do |arb_file|
|
155
|
-
unless arb_file == l10n_messages_file
|
156
|
-
UI.message("Verifying #{arb_file}...")
|
157
|
-
errors = Helper::FlutterHelper.compare_arb(l10n_messages_file,
|
158
|
-
arb_file)
|
159
|
-
if errors.any?
|
160
|
-
errors.each { |e| UI.error(e) }
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
UI.user_error!('Found inconsistencies in ARB files') if errors_found
|
165
|
-
end
|
166
|
-
|
167
|
-
unless params[:l10n_strings_locale]
|
168
|
-
# Don't generate .dart for the original ARB unless it has its own
|
169
|
-
# locale.
|
170
|
-
arb_files.delete(l10n_messages_file)
|
171
|
-
end
|
172
|
-
|
173
|
-
if params[:l10n_reformat_arb]
|
174
|
-
arb_files.each do |arb_file|
|
175
|
-
UI.message("Reformatting file #{arb_file}...")
|
176
|
-
Helper::FlutterHelper.reformat_arb(arb_file)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
sh *%W(flutter packages pub run intl_translation:generate_from_arb
|
181
|
-
--output-dir=#{output_dir}
|
182
|
-
--no-use-deferred-loading
|
183
|
-
#{params[:l10n_strings_file]}) + arb_files
|
11
|
+
Helper::FlutterHelper.flutter(*params[:args])
|
184
12
|
end
|
185
13
|
|
186
14
|
def self.description
|
187
|
-
"
|
188
|
-
end
|
189
|
-
|
190
|
-
def self.authors
|
191
|
-
["Artem Sheremet"]
|
192
|
-
end
|
193
|
-
|
194
|
-
def self.return_value
|
195
|
-
'For "build" action, the return value is a mapping of fastlane ' \
|
196
|
-
'platform names into built output files, e.g.: ' +
|
197
|
-
{ android: '/Users/foo/src/flutter/build/outputs/myapp.apk' }.inspect
|
198
|
-
end
|
199
|
-
|
200
|
-
def self.details
|
201
|
-
# Optional:
|
202
|
-
""
|
15
|
+
'Run "flutter" binary with the specified arguments'
|
203
16
|
end
|
204
17
|
|
205
18
|
def self.available_options
|
206
19
|
[
|
207
20
|
FastlaneCore::ConfigItem.new(
|
208
|
-
key: :
|
209
|
-
env_name: '
|
210
|
-
description: '
|
211
|
-
|
212
|
-
type: String,
|
213
|
-
verify_block: proc do |value|
|
214
|
-
unless FLUTTER_ACTIONS.include?(value)
|
215
|
-
UI.user_error!("Supported actions are: #{FLUTTER_ACTIONS}")
|
216
|
-
end
|
217
|
-
end,
|
218
|
-
),
|
219
|
-
FastlaneCore::ConfigItem.new(
|
220
|
-
key: :debug,
|
221
|
-
env_name: 'FL_FLUTTER_DEBUG',
|
222
|
-
description: 'Build a Debug version of the app if true',
|
223
|
-
optional: true,
|
224
|
-
is_string: false,
|
225
|
-
default_value: false,
|
226
|
-
),
|
227
|
-
FastlaneCore::ConfigItem.new(
|
228
|
-
key: :codesign,
|
229
|
-
env_name: 'FL_FLUTTER_CODESIGN',
|
230
|
-
description: 'Set to false to skip iOS app signing. This may be ' \
|
231
|
-
'useful e.g. on CI or when signed by Fastlane "sigh"',
|
232
|
-
optional: true,
|
233
|
-
is_string: false,
|
234
|
-
default_value: true,
|
235
|
-
),
|
236
|
-
FastlaneCore::ConfigItem.new(
|
237
|
-
key: :build_number_override,
|
238
|
-
env_name: 'FL_FLUTTER_BUILD_NUMBER_OVERRIDE',
|
239
|
-
description: <<-'DESCRIPTION',
|
240
|
-
Override build number in pubspec.yaml. Can be either a number, or
|
241
|
-
a schema definition, ex.: ci (take from CI) vcs (take from Git),
|
242
|
-
ci+1000 (take from CI and add 1000).
|
243
|
-
DESCRIPTION
|
244
|
-
optional: true,
|
245
|
-
verify_block: Helper::FlutterHelper.method(:build_number),
|
246
|
-
),
|
247
|
-
FastlaneCore::ConfigItem.new(
|
248
|
-
key: :build_name_override,
|
249
|
-
env_name: 'FL_FLUTTER_BUILD_NAME_OVERRIDE',
|
250
|
-
description: <<-'DESCRIPTION',
|
251
|
-
Override build name in pubspec.yaml. Can be either a string, or a
|
252
|
-
schema definition, ex.: vcs* (take from VCS, add "*" for dirty
|
253
|
-
tree), vcs (take from VCS, no dirty mark), ^vcs (take from the
|
254
|
-
latest VCS tag which must be in the form X.Y, and add the number
|
255
|
-
of commits since that tag as the third number, unless it's zero).
|
256
|
-
NOTE: for App Store, build name must be in the format of at most 3
|
257
|
-
integeres separated by a dot (".").
|
258
|
-
DESCRIPTION
|
259
|
-
optional: true,
|
260
|
-
verify_block: Helper::FlutterHelper.method(:build_name),
|
261
|
-
),
|
262
|
-
# l10n settings
|
263
|
-
FastlaneCore::ConfigItem.new(
|
264
|
-
key: :l10n_strings_file,
|
265
|
-
env_name: 'FL_FLUTTER_L10N_STRINGS',
|
266
|
-
description: 'Path to the .dart file with l10n strings',
|
267
|
-
optional: true,
|
268
|
-
verify_block: proc do |value|
|
269
|
-
UI.user_error!('File does not exist') unless File.exist?(value)
|
270
|
-
end,
|
271
|
-
),
|
272
|
-
FastlaneCore::ConfigItem.new(
|
273
|
-
key: :l10n_strings_locale,
|
274
|
-
env_name: 'FL_FLUTTER_L10N_STRINGS_LOCALE',
|
275
|
-
description: 'Locale of the data in l10n_strings_file',
|
276
|
-
optional: true,
|
277
|
-
),
|
278
|
-
FastlaneCore::ConfigItem.new(
|
279
|
-
key: :l10n_reformat_arb,
|
280
|
-
env_name: 'FL_FLUTTER_L10N_REFORMAT_ARB',
|
281
|
-
description: 'Reformat .arb files',
|
282
|
-
optional: true,
|
283
|
-
is_string: false,
|
284
|
-
default_value: false,
|
285
|
-
),
|
286
|
-
FastlaneCore::ConfigItem.new(
|
287
|
-
key: :l10n_verify_arb,
|
288
|
-
env_name: 'FL_FLUTTER_L10N_VERIFY_ARB',
|
289
|
-
description: 'Verify that each .arb file includes all strings',
|
290
|
-
optional: true,
|
291
|
-
is_string: false,
|
292
|
-
default_value: true,
|
293
|
-
),
|
294
|
-
FastlaneCore::ConfigItem.new(
|
295
|
-
key: :android_licenses,
|
296
|
-
env_name: 'FL_FLUTTER_ANDROID_LICENSES',
|
297
|
-
description: 'Hashes of accepted Android SDK linceses, which may ' \
|
298
|
-
'be found in $ANDROID_SDK_ROOT/licenses',
|
299
|
-
optional: true,
|
300
|
-
is_string: false,
|
301
|
-
verify_block: proc { |value| value.kind_of?(Hash) },
|
302
|
-
default_value: {},
|
21
|
+
key: :args,
|
22
|
+
env_name: 'FL_FLUTTER_ARGS',
|
23
|
+
description: 'Arguments to Flutter command',
|
24
|
+
type: Array,
|
303
25
|
),
|
304
26
|
]
|
305
27
|
end
|
306
|
-
|
307
|
-
def self.is_supported?(platform)
|
308
|
-
# Also support nil (root lane).
|
309
|
-
[nil, :ios, :android].include?(platform)
|
310
|
-
end
|
311
28
|
end
|
312
29
|
end
|
313
30
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../base/flutter_action_base'
|
3
|
+
require_relative '../helper/flutter_helper'
|
4
|
+
require_relative '../helper/flutter_bootstrap_helper'
|
5
|
+
|
6
|
+
module Fastlane
|
7
|
+
module Actions
|
8
|
+
class FlutterBootstrapAction < Action
|
9
|
+
extend FlutterActionBase
|
10
|
+
|
11
|
+
def self.run(params)
|
12
|
+
if params[:android_licenses]
|
13
|
+
Helper::FlutterBootstrapHelper.accept_licenses(
|
14
|
+
File.join(android_sdk_root!, 'licenses'),
|
15
|
+
params[:android_licenses],
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Upgrade or install Flutter SDK.
|
20
|
+
flutter_sdk_root = Helper::FlutterHelper.flutter_sdk_root
|
21
|
+
flutter_channel = params[:flutter_channel]
|
22
|
+
if File.directory?(flutter_sdk_root)
|
23
|
+
UI.message("Upgrading Flutter SDK in #{flutter_sdk_root}...")
|
24
|
+
if flutter_channel
|
25
|
+
UI.message("Making sure Flutter is on channel #{flutter_channel}")
|
26
|
+
Helper::FlutterHelper.flutter(
|
27
|
+
'channel', flutter_channel, log: false
|
28
|
+
)
|
29
|
+
end
|
30
|
+
Helper::FlutterHelper.flutter('upgrade', log: false)
|
31
|
+
else
|
32
|
+
Helper::FlutterHelper.git(
|
33
|
+
'clone', # no --depth to keep Flutter tag-based versioning.
|
34
|
+
"--branch=#{flutter_channel || 'beta'}",
|
35
|
+
'--',
|
36
|
+
'https://github.com/flutter/flutter.git',
|
37
|
+
flutter_sdk_root,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
UI.message('Precaching Flutter SDK binaries...')
|
41
|
+
Helper::FlutterHelper.flutter('precache', log: false)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.android_sdk_root!
|
45
|
+
(ENV['ANDROID_HOME'] || ENV['ANDROID_SDK_ROOT']).tap do |path|
|
46
|
+
unless path
|
47
|
+
UI.build_failure!('Android SDK directory environment variables ' \
|
48
|
+
'are not set. See ' \
|
49
|
+
'https://developer.android.com/studio/command-line/variables')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.description
|
55
|
+
'Flutter SDK installation, upgrade and application bootstrap'
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.available_options
|
59
|
+
[
|
60
|
+
FastlaneCore::ConfigItem.new(
|
61
|
+
key: :flutter_channel,
|
62
|
+
env_name: 'FL_FLUTTER_CHANNEL',
|
63
|
+
description: 'Flutter SDK channel (keep existing if unset)',
|
64
|
+
optional: true,
|
65
|
+
type: String,
|
66
|
+
),
|
67
|
+
FastlaneCore::ConfigItem.new(
|
68
|
+
key: :android_licenses,
|
69
|
+
description: 'Map of file names to hash values of accepted ' \
|
70
|
+
'Android SDK linceses, which may be found in ' \
|
71
|
+
'$ANDROID_SDK_ROOT/licenses/ on developer workstations. Gradle ' \
|
72
|
+
'will refuse to install SDK unless licenses are accepted',
|
73
|
+
optional: true,
|
74
|
+
type: Hash,
|
75
|
+
),
|
76
|
+
]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../base/flutter_action_base'
|
3
|
+
require_relative '../helper/flutter_helper'
|
4
|
+
|
5
|
+
module Fastlane
|
6
|
+
module Actions
|
7
|
+
module SharedValues
|
8
|
+
FLUTTER_OUTPUT = :FLUTTER_OUTPUT
|
9
|
+
end
|
10
|
+
|
11
|
+
class FlutterBuildAction < Action
|
12
|
+
extend FlutterActionBase
|
13
|
+
|
14
|
+
FASTLANE_PLATFORM_TO_BUILD = {
|
15
|
+
ios: 'ios',
|
16
|
+
android: 'apk',
|
17
|
+
}
|
18
|
+
|
19
|
+
def self.run(params)
|
20
|
+
# "flutter build" args list.
|
21
|
+
build_args = []
|
22
|
+
|
23
|
+
if params[:build]
|
24
|
+
build_args.push(params[:build])
|
25
|
+
else
|
26
|
+
if fastlane_platform = (lane_context[SharedValues::PLATFORM_NAME] ||
|
27
|
+
lane_context[SharedValues::DEFAULT_PLATFORM])
|
28
|
+
build_args.push(FASTLANE_PLATFORM_TO_BUILD[fastlane_platform])
|
29
|
+
else
|
30
|
+
UI.user_error!('flutter_build action "build" parameter is not ' \
|
31
|
+
'specified and cannot be inferred from Fastlane context.')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if params[:debug]
|
36
|
+
build_args.push('--debug')
|
37
|
+
end
|
38
|
+
|
39
|
+
if params[:codesign] == false
|
40
|
+
build_args.push('--no-codesign')
|
41
|
+
end
|
42
|
+
|
43
|
+
if build_number = (params[:build_number] ||
|
44
|
+
lane_context[SharedValues::BUILD_NUMBER])
|
45
|
+
build_args.push('--build-number', build_number.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
if build_name = (params[:build_name] ||
|
49
|
+
lane_context[SharedValues::VERSION_NUMBER])
|
50
|
+
build_args.push('--build-name', build_name.to_s)
|
51
|
+
end
|
52
|
+
|
53
|
+
Helper::FlutterHelper.flutter('build', *build_args) do |status, res|
|
54
|
+
if status.success?
|
55
|
+
if res =~ /^Built (.*?)(:? \([^)]*\))?\.$/
|
56
|
+
lane_context[SharedValues::FLUTTER_OUTPUT] =
|
57
|
+
File.absolute_path($1)
|
58
|
+
else
|
59
|
+
UI.important('Cannot parse built file path from "flutter build"')
|
60
|
+
end
|
61
|
+
else
|
62
|
+
# fastlane does not fail automatically if we provide a block.
|
63
|
+
UI.build_failure!('"flutter build" has failed')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
lane_context[SharedValues::FLUTTER_OUTPUT]
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.description
|
71
|
+
'Run "flutter build" to build a Flutter application'
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.return_value
|
75
|
+
'A path to the built file, if available'
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.available_options
|
79
|
+
[
|
80
|
+
FastlaneCore::ConfigItem.new(
|
81
|
+
key: :build,
|
82
|
+
env_name: 'FL_FLUTTER_BUILD',
|
83
|
+
description: 'Type of Flutter build (e.g. apk, appbundle, ios)',
|
84
|
+
optional: true,
|
85
|
+
type: String,
|
86
|
+
),
|
87
|
+
FastlaneCore::ConfigItem.new(
|
88
|
+
key: :debug,
|
89
|
+
env_name: 'FL_FLUTTER_DEBUG',
|
90
|
+
description: 'Build a Debug version of the app if true',
|
91
|
+
optional: true,
|
92
|
+
type: Boolean,
|
93
|
+
default_value: false,
|
94
|
+
),
|
95
|
+
FastlaneCore::ConfigItem.new(
|
96
|
+
key: :codesign,
|
97
|
+
env_name: 'FL_FLUTTER_CODESIGN',
|
98
|
+
description: 'Set to false to skip iOS app signing. This may be ' \
|
99
|
+
'useful e.g. on CI or when signed later by Fastlane "sigh"',
|
100
|
+
optional: true,
|
101
|
+
type: Boolean,
|
102
|
+
),
|
103
|
+
FastlaneCore::ConfigItem.new(
|
104
|
+
key: :build_number,
|
105
|
+
env_name: 'FL_FLUTTER_BUILD_NUMBER',
|
106
|
+
description: 'Override build number specified in pubspec.yaml',
|
107
|
+
optional: true,
|
108
|
+
type: Integer,
|
109
|
+
),
|
110
|
+
FastlaneCore::ConfigItem.new(
|
111
|
+
key: :build_name,
|
112
|
+
env_name: 'FL_FLUTTER_BUILD_NAME',
|
113
|
+
description: <<-'DESCRIPTION',
|
114
|
+
Override build name specified in pubspec.yaml.
|
115
|
+
NOTE: for App Store, build name must be in the format of at most 3
|
116
|
+
integeres separated by a dot (".").
|
117
|
+
DESCRIPTION
|
118
|
+
optional: true,
|
119
|
+
),
|
120
|
+
]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
require_relative '../base/flutter_action_base'
|
3
|
+
require_relative '../helper/flutter_helper'
|
4
|
+
require_relative '../helper/flutter_generate_intl_helper'
|
5
|
+
require_relative '../helper/flutter_generate_build_runner_helper'
|
6
|
+
|
7
|
+
module Fastlane
|
8
|
+
module Actions
|
9
|
+
class FlutterGenerateAction < Action
|
10
|
+
extend FlutterActionBase
|
11
|
+
|
12
|
+
def self.run(params)
|
13
|
+
Helper::FlutterHelper.flutter(*%w(packages get), log: false)
|
14
|
+
|
15
|
+
# In an ideal world, this should be a part of build_runner:
|
16
|
+
# https://github.com/dart-lang/intl_translation/issues/32
|
17
|
+
# Generate Intl messages before others, since these are static and
|
18
|
+
# others may be not.
|
19
|
+
if generate_translation?
|
20
|
+
Helper::FlutterGenerateIntlHelper.generate(
|
21
|
+
params[:intl_strings_file], params[:intl_strings_locale]
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
if Helper::FlutterHelper.dev_dependency?('build_runner')
|
26
|
+
UI.message('Found build_runner dependency, running build...')
|
27
|
+
Helper::FlutterGenerateBuildRunnerHelper.build
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.generate_translation?
|
32
|
+
Helper::FlutterHelper.dev_dependency?('intl_translation')
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.description
|
36
|
+
'According to package:intl, take $strings_file and generate ' \
|
37
|
+
'${strings_file.dirname}/arb/intl_messages.arb, then take all files ' \
|
38
|
+
'matching ${strings_file.dirname}/intl_*.arb, fix them and generate ' \
|
39
|
+
'.dart files from them'
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.available_options
|
43
|
+
# https://docs.fastlane.tools/advanced/actions/#configuration-files
|
44
|
+
[
|
45
|
+
FastlaneCore::ConfigItem.new(
|
46
|
+
key: :intl_strings_file,
|
47
|
+
env_name: 'FL_FLUTTER_INTL_STRINGS_FILE',
|
48
|
+
description: 'Path to source .dart file with Intl.message calls',
|
49
|
+
verify_block: proc do |value|
|
50
|
+
if generate_translation?
|
51
|
+
unless File.exist?(value)
|
52
|
+
UI.user_error!("File `#{value}' does not exist")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end,
|
56
|
+
default_value: 'lib/intl/intl.dart',
|
57
|
+
),
|
58
|
+
FastlaneCore::ConfigItem.new(
|
59
|
+
key: :intl_strings_locale,
|
60
|
+
env_name: 'FL_FLUTTER_INTL_STRINGS_LOCALE',
|
61
|
+
description: 'Locale of the default data in the strings_file',
|
62
|
+
optional: true,
|
63
|
+
),
|
64
|
+
]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'fastlane/action'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Actions
|
5
|
+
module FlutterActionBase
|
6
|
+
def authors
|
7
|
+
['github.com/dotdoom']
|
8
|
+
end
|
9
|
+
|
10
|
+
def is_supported?(platform)
|
11
|
+
# Also support nil (root lane).
|
12
|
+
[nil, :ios, :android].include?(platform)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'fastlane_core/ui/ui'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Fastlane
|
5
|
+
module Helper
|
6
|
+
class FlutterBootstrapHelper
|
7
|
+
def self.accept_licenses(licenses_directory, licenses)
|
8
|
+
FileUtils.mkdir_p(licenses_directory)
|
9
|
+
licenses.each_pair do |license, hash|
|
10
|
+
license_file = File.join(licenses_directory, license)
|
11
|
+
next if File.exist?(license_file) &&
|
12
|
+
File.readlines(license_file).map(&:strip).include?(hash)
|
13
|
+
UI.message("Updating Android SDK license in #{license_file}...")
|
14
|
+
File.open(license_file, 'a') { |f| f.puts('', hash) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Fastlane
|
4
|
+
module Helper
|
5
|
+
class FlutterGenerateBuildRunnerHelper
|
6
|
+
def self.build
|
7
|
+
Helper::FlutterHelper.flutter(
|
8
|
+
*%w(packages pub run build_runner build --delete-conflicting-outputs),
|
9
|
+
log: false,
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'set'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
require_relative 'flutter_helper'
|
7
|
+
|
8
|
+
module Fastlane
|
9
|
+
module Helper
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Fastlane::Helper::FlutterGenerateIntlHelper
|
14
|
+
def self.generate(messages_filename, messages_locale = nil)
|
15
|
+
dart_files_dirname = File.dirname(messages_filename)
|
16
|
+
arb_files_dirname = File.join(dart_files_dirname, 'arb')
|
17
|
+
full_arb_filename = generate_arb_from_dart(
|
18
|
+
messages_filename, messages_locale, arb_files_dirname
|
19
|
+
)
|
20
|
+
|
21
|
+
arb_filenames = amend_arb_files(arb_files_dirname, full_arb_filename)
|
22
|
+
|
23
|
+
unless messages_locale
|
24
|
+
# Don't generate .dart for the ARB generated from original, unless it
|
25
|
+
# has its own locale.
|
26
|
+
arb_filenames.delete(full_arb_filename)
|
27
|
+
end
|
28
|
+
|
29
|
+
Fastlane::UI.message('Generating .dart files from .arb...')
|
30
|
+
Fastlane::Helper::FlutterHelper.flutter(
|
31
|
+
*%W(packages pub run intl_translation:generate_from_arb
|
32
|
+
--output-dir=#{dart_files_dirname}
|
33
|
+
--no-use-deferred-loading
|
34
|
+
#{messages_filename}) + arb_filenames
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.amend_arb_files(arb_files_dirname, full_arb_filename)
|
39
|
+
full_arb_json = JSON.parse(File.read(full_arb_filename))
|
40
|
+
|
41
|
+
# Sort files for consistency, because generated messages_all.dart will
|
42
|
+
# have imports ordered as in the command line below.
|
43
|
+
arb_filenames = Dir.glob(File.join(arb_files_dirname, 'intl_*.arb')).sort
|
44
|
+
arb_filenames.each do |arb_filename|
|
45
|
+
arb_json = JSON.parse(File.read(arb_filename))
|
46
|
+
if arb_filename != full_arb_filename
|
47
|
+
Fastlane::UI.message("Amending #{arb_filename}...")
|
48
|
+
full_arb_json.each_pair do |k, v|
|
49
|
+
# Ignore @@keys. We don't want to copy @@locale over all files, and
|
50
|
+
# it's often unspecified to be inferred from file name.
|
51
|
+
arb_json[k] ||= v unless k.start_with?('@@')
|
52
|
+
end
|
53
|
+
arb_json.keep_if { |k| full_arb_json.key?(k) }
|
54
|
+
end
|
55
|
+
File.write(arb_filename, JSON.pretty_generate(arb_json) + "\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.generate_arb_from_dart(dart_filename, dart_locale, arb_dirname)
|
60
|
+
arb_filename = File.join(arb_dirname, 'intl_messages.arb')
|
61
|
+
Fastlane::UI.message("Generating #{arb_filename} from #{dart_filename}...")
|
62
|
+
|
63
|
+
if File.exist?(arb_filename)
|
64
|
+
arb_file_was = File.read(arb_filename)
|
65
|
+
else
|
66
|
+
# The file may not exist on the first run. Then it's also probable that
|
67
|
+
# the output directory does not exist yet.
|
68
|
+
FileUtils.mkdir_p(arb_dirname)
|
69
|
+
end
|
70
|
+
|
71
|
+
extract_to_arb_options = ["--output-dir=#{arb_dirname}"]
|
72
|
+
if dart_locale
|
73
|
+
extract_to_arb_options.push("--locale=#{dart_locale}")
|
74
|
+
end
|
75
|
+
|
76
|
+
Fastlane::Helper::FlutterHelper.flutter(
|
77
|
+
*%w(packages pub run intl_translation:extract_to_arb),
|
78
|
+
*extract_to_arb_options, dart_filename
|
79
|
+
)
|
80
|
+
|
81
|
+
# intl will update @@last_modified even if there are no updates; this
|
82
|
+
# leaves Git directory unnecessary dirty. If that's the only change,
|
83
|
+
# just restore the previous contents.
|
84
|
+
if arb_file_was
|
85
|
+
if restore_last_modified(arb_filename, arb_file_was)
|
86
|
+
Fastlane::UI.message(
|
87
|
+
"@@last_modified has been restored in #{arb_filename}"
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
arb_filename
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.restore_last_modified(filename, old_content)
|
96
|
+
new_content_tree = JSON.parse(File.read(filename))
|
97
|
+
old_content_tree = JSON.parse(old_content)
|
98
|
+
new_content_tree['@@last_modified'] = old_content_tree['@@last_modified']
|
99
|
+
|
100
|
+
# Use to_json to compare the objects deep and in consistent format.
|
101
|
+
if new_content_tree.to_json == old_content_tree.to_json
|
102
|
+
# Except for the @@last_modified attribute that we replaced
|
103
|
+
# above, the objects are identical. Restore previous timestamp.
|
104
|
+
File.write(filename, old_content)
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
|
108
|
+
false
|
109
|
+
end
|
110
|
+
end
|
@@ -1,147 +1,35 @@
|
|
1
|
-
require '
|
2
|
-
require 'fileutils'
|
3
|
-
require 'json'
|
4
|
-
require 'set'
|
5
|
-
require 'tempfile'
|
1
|
+
require 'yaml'
|
6
2
|
|
7
3
|
module Fastlane
|
8
|
-
# UI = FastlaneCore::UI unless Fastlane.const_defined?("UI")
|
9
|
-
|
10
4
|
module Helper
|
11
5
|
class FlutterHelper
|
12
|
-
def self.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# Use to_json to compare the objects deep and in consistent format.
|
20
|
-
if new_content_tree.to_json == old_content_tree.to_json
|
21
|
-
# Except for the @@last_modified attribute that we replaced
|
22
|
-
# above, the objects are identical. Restore previous timestamp.
|
23
|
-
File.write(file_name, old_content)
|
24
|
-
return true
|
25
|
-
end
|
26
|
-
|
27
|
-
false
|
28
|
-
end
|
29
|
-
|
30
|
-
def self.reformat_arb(file_name)
|
31
|
-
pretty_content = JSON.pretty_generate(JSON.parse(File.read(file_name)))
|
32
|
-
|
33
|
-
File.write(file_name, pretty_content + "\n")
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.compare_arb(origin, sample)
|
37
|
-
is_significant_key = ->(key) { !key.start_with?('@') }
|
38
|
-
|
39
|
-
origin_keys = Set.new(JSON.parse(File.read(origin))
|
40
|
-
.keys
|
41
|
-
.select(&is_significant_key))
|
42
|
-
sample_keys = Set.new(JSON.parse(File.read(sample))
|
43
|
-
.keys
|
44
|
-
.select(&is_significant_key))
|
45
|
-
|
46
|
-
keys_not_in_sample = origin_keys - sample_keys
|
47
|
-
keys_not_in_origin = sample_keys - origin_keys
|
48
|
-
|
49
|
-
differencies = []
|
50
|
-
if keys_not_in_sample.any?
|
51
|
-
differencies.push("Translation string(s): " \
|
52
|
-
"#{keys_not_in_sample.to_a.join(', ')}; " \
|
53
|
-
"are missing")
|
54
|
-
end
|
55
|
-
if keys_not_in_origin.any?
|
56
|
-
differencies.push("Translation string(s): " \
|
57
|
-
"#{keys_not_in_origin.to_a.join(', ')}; " \
|
58
|
-
"are unused")
|
59
|
-
end
|
60
|
-
differencies
|
6
|
+
def self.flutter(*argv, &block)
|
7
|
+
# TODO(dotdoom): explain special keys in params, like "log" etc.
|
8
|
+
# TODO(dotdoom): most commands set "log: false", which means that the
|
9
|
+
# output is lost (even in case of error). Perhaps we
|
10
|
+
# could print the output here, via error_callback?
|
11
|
+
# https://github.com/fastlane/fastlane/blob/b1495d134eec6681c8d7a544aa3520f1da00c80e/fastlane/lib/fastlane/helper/sh_helper.rb#L73
|
12
|
+
Actions.sh(File.join(flutter_sdk_root, 'bin', 'flutter'), *argv, &block)
|
61
13
|
end
|
62
14
|
|
63
|
-
def self.
|
64
|
-
|
65
|
-
rescue ArgumentError
|
66
|
-
build_number_source, build_number_base = schema.split('+', 2)
|
67
|
-
|
68
|
-
case build_number_source
|
69
|
-
when 'vcs'
|
70
|
-
build_number = Integer(
|
71
|
-
Actions.sh(*%w(git rev-list --count HEAD)).strip
|
72
|
-
)
|
73
|
-
when 'ci'
|
74
|
-
begin
|
75
|
-
build_number = Integer(ENV['TRAVIS_BUILD_NUMBER'] ||
|
76
|
-
ENV['CIRCLE_BUILD_NUM'])
|
77
|
-
rescue ArgumentError, TypeError
|
78
|
-
raise if ENV.key?('CI')
|
79
|
-
raise ArgumentError, 'CI version requested, but not running on a CI'
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
if build_number_base
|
84
|
-
build_number + Integer(build_number_base)
|
85
|
-
else
|
86
|
-
build_number
|
87
|
-
end
|
15
|
+
def self.git(*argv, &block)
|
16
|
+
Actions.sh('git', *argv, &block)
|
88
17
|
end
|
89
18
|
|
90
|
-
def self.
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
# We try to match, otherwise assume vcs_version = $TAG, which means
|
99
|
-
# that there are 0 commits since the latest tag.
|
100
|
-
if schema_caret && vcs_version =~ /^
|
101
|
-
(\d+[.]\d+)-
|
102
|
-
(\d+)-
|
103
|
-
g.*?
|
104
|
-
(#{Regexp.quote(schema_dirty_mark)})?
|
105
|
-
$/x
|
106
|
-
|
107
|
-
vcs_version = "#{$1}.#{$2}"
|
108
|
-
# Making the best guess whether it's dirty.
|
109
|
-
if $3
|
110
|
-
vcs_version += schema_dirty_mark
|
111
|
-
end
|
19
|
+
def self.flutter_sdk_root
|
20
|
+
@flutter_sdk_root ||= File.expand_path(
|
21
|
+
if ENV.include?('FLUTTER_SDK_ROOT')
|
22
|
+
ENV['FLUTTER_SDK_ROOT']
|
23
|
+
elsif flutter_binary = FastlaneCore::CommandExecutor.which('flutter')
|
24
|
+
File.dirname(File.dirname(flutter_binary))
|
25
|
+
else
|
26
|
+
'vendor/flutter'
|
112
27
|
end
|
113
|
-
|
114
|
-
vcs_version
|
115
|
-
else
|
116
|
-
schema
|
117
|
-
end
|
28
|
+
)
|
118
29
|
end
|
119
30
|
|
120
|
-
|
121
|
-
|
122
|
-
def self.bootstrap_android(licenses = {})
|
123
|
-
# Gradle will refuse to install Android SDK unless the SDK directory
|
124
|
-
# exists and licenses are accepted.
|
125
|
-
licenses_directory = File.join(android_sdk_root!, 'licenses')
|
126
|
-
FileUtils.mkdir_p(licenses_directory)
|
127
|
-
if licenses.any?
|
128
|
-
licenses.each_pair do |license, hash|
|
129
|
-
license_file = File.join(licenses_directory, license)
|
130
|
-
unless File.exist?(license_file) &&
|
131
|
-
File.readlines(license_file).map(&:strip).include?(hash)
|
132
|
-
File.open(license_file, 'a') { |f| f.puts('', hash) }
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def self.android_sdk_root!
|
139
|
-
(ENV['ANDROID_HOME'] || ENV['ANDROID_SDK_ROOT']).tap do |path|
|
140
|
-
unless path
|
141
|
-
raise 'Android SDK directory environment variables are not set. ' \
|
142
|
-
'See https://developer.android.com/studio/command-line/variables'
|
143
|
-
end
|
144
|
-
end
|
31
|
+
def self.dev_dependency?(package)
|
32
|
+
(YAML.load_file('pubspec.yaml')['dev_dependencies'] || {}).key?(package)
|
145
33
|
end
|
146
34
|
end
|
147
35
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-flutter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artem Sheremet
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -146,6 +146,13 @@ files:
|
|
146
146
|
- README.md
|
147
147
|
- lib/fastlane/plugin/flutter.rb
|
148
148
|
- lib/fastlane/plugin/flutter/actions/flutter_action.rb
|
149
|
+
- lib/fastlane/plugin/flutter/actions/flutter_bootstrap_action.rb
|
150
|
+
- lib/fastlane/plugin/flutter/actions/flutter_build_action.rb
|
151
|
+
- lib/fastlane/plugin/flutter/actions/flutter_generate_action.rb
|
152
|
+
- lib/fastlane/plugin/flutter/base/flutter_action_base.rb
|
153
|
+
- lib/fastlane/plugin/flutter/helper/flutter_bootstrap_helper.rb
|
154
|
+
- lib/fastlane/plugin/flutter/helper/flutter_generate_build_runner_helper.rb
|
155
|
+
- lib/fastlane/plugin/flutter/helper/flutter_generate_intl_helper.rb
|
149
156
|
- lib/fastlane/plugin/flutter/helper/flutter_helper.rb
|
150
157
|
- lib/fastlane/plugin/flutter/version.rb
|
151
158
|
homepage: https://github.com/dotdoom/fastlane-plugin-flutter
|