fastlane-plugin-flutter 0.2.7 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|