fastlane-plugin-android_channels 0.2.0 → 0.3.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e53b39ed711bb97fe656417eec498e5631621bdab69f93efcd8e32fad9b20151
|
4
|
+
data.tar.gz: 38082a02a372af9be715e6ef528362be2fd9b10a07add4c297837f71897d3194
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 620e7c463d504063ddb04ee661c25bb4070061af25091f9a37c1224aa8106bbe447604bfc12eb4a70fd9c4ddb7f26319ca66bd1fc6219160d5998e2f3666e7ac
|
7
|
+
data.tar.gz: ddcd417932a5d239aae68a56fb2004d8dfc8fec3fca0df027c6d850458b0b345ce51b071b769f078c77e444eb6d2a81d12389c74db3d8c45a7da70c7eb9acf18
|
data/README.md
CHANGED
@@ -12,12 +12,56 @@ fastlane add_plugin android_channels
|
|
12
12
|
|
13
13
|
## About android_channels
|
14
14
|
|
15
|
-
Package unsign apk with channels
|
15
|
+
Package unsign apk with channels and write empty file to META-INF with channel in general way
|
16
16
|
|
17
|
-
|
17
|
+
## Configuration
|
18
|
+
|
19
|
+
```
|
20
|
+
+-------------------------+-------------------------------------------------+------------------------------------------+-------------------------------------------------+
|
21
|
+
| android_channels Options |
|
22
|
+
+-------------------------+-------------------------------------------------+------------------------------------------+-------------------------------------------------+
|
23
|
+
| Key | Description | Env Var | Default |
|
24
|
+
+-------------------------+-------------------------------------------------+------------------------------------------+-------------------------------------------------+
|
25
|
+
| apk_file | The path of apk file | ANDROID_CHANNELS_APK_FILE | builds/output/apk/app-unsigned.apk |
|
26
|
+
| channels | The key password of keystore | ANDROID_CHANNELS_CHANNELS | [] |
|
27
|
+
| channel_file | The path of channel file, accepts json, yaml | ANDROID_CHANNELS_CHANNEL_FILE | |
|
28
|
+
| | and plain text file (split with space, comma | | |
|
29
|
+
| | and newline) | | |
|
30
|
+
| channel_filename_prefix | The prefix of empty channel file to write to | ANDROID_CHANNELS_CHANNEL_FILENAME_PREFIX | |
|
31
|
+
| | METE-INF folder | | |
|
32
|
+
| channel_filename_suffix | The suffix of empty channel file to write to | ANDROID_CHANNELS_CHANNEL_FILENAME_SUFFIX | |
|
33
|
+
| | METE-INF folder | | |
|
34
|
+
| output_path | The output path of channel apk files | ANDROID_CHANNELS_OUTPUT_PATH | channel_apks |
|
35
|
+
| android_sdk_path | The path of android sdk | ANDROID_CHANNELS_ANDROID_SDK_PATH | /usr/local/share/android-sdk |
|
36
|
+
| build_tools_version | The version of build tools (by default, always | ANDROID_CHANNELS_BUILD_TOOLS_VERSION | |
|
37
|
+
| | use latest version) | | |
|
38
|
+
| keystore | The path of keystore file | ANDROID_CHANNELS_KEYSTORE | release.keystore |
|
39
|
+
| keystore_password | The password of keystore | ANDROID_CHANNELS_KEYSTORE_PASSWORD | |
|
40
|
+
| key_alias | The key alias of keystore | ANDROID_CHANNELS_KEY_ALIAS | |
|
41
|
+
| key_password | The key password of keystore | ANDROID_CHANNELS_KEY_PASSWORD | |
|
42
|
+
| apksigner_extra_args | The extra arguments of apksigner command | ANDROID_CHANNELS_APKSIGNER_EXTRA_ARGS | |
|
43
|
+
| verify | Do or not verify signed apk file | ANDROID_CHANNELS_VERIFY | false |
|
44
|
+
| clean | Should the signed files to be clean before | ANDROID_CHANNELS_CLEAN | false |
|
45
|
+
| | signing it? | | |
|
46
|
+
+-------------------------+-------------------------------------------------+------------------------------------------+-------------------------------------------------+
|
47
|
+
* = default value is dependent on the user's system
|
48
|
+
```
|
49
|
+
|
50
|
+
Here has some example [channel files](examples/).
|
51
|
+
|
52
|
+
## Return value
|
53
|
+
|
54
|
+
```
|
55
|
+
+-------------------------------+
|
56
|
+
| android_channels Return Value |
|
57
|
+
+-------------------------------+
|
58
|
+
| The output of signed apk path |
|
59
|
+
+-------------------------------+
|
60
|
+
```
|
18
61
|
|
19
62
|
## Example
|
20
63
|
|
64
|
+
|
21
65
|
Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
|
22
66
|
|
23
67
|
**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)
|
@@ -10,6 +10,8 @@ module Fastlane
|
|
10
10
|
apk_file = Helper::AndroidChannelsHelper.determine_apk_file!(params)
|
11
11
|
channels = Helper::AndroidChannelsHelper.determine_channels!(params)
|
12
12
|
output_path = Helper::AndroidChannelsHelper.determine_output_path!(params)
|
13
|
+
apksigner = Helper::AndroidChannelsHelper.determine_apksigner!(params)
|
14
|
+
apksigner_args = nil
|
13
15
|
|
14
16
|
signed = Helper::AndroidChannelsHelper.is_signed?(apk_file)
|
15
17
|
summary_params = {
|
@@ -21,17 +23,12 @@ module Fastlane
|
|
21
23
|
|
22
24
|
unless signed
|
23
25
|
keystore = Helper::AndroidChannelsHelper.determine_keystore!(params)
|
24
|
-
|
25
|
-
keystore_password = Helper::AndroidChannelsHelper.determine_keystore_password!(params)
|
26
|
-
key_alias = params[:key_alias]
|
27
|
-
key_password = params[:key_password]
|
26
|
+
apksigner_args = Helper::AndroidChannelsHelper.apksigner_args(params)
|
28
27
|
|
29
28
|
summary_params.merge!({
|
30
|
-
apksigner: apksigner,
|
31
29
|
keystore: keystore,
|
32
|
-
|
33
|
-
|
34
|
-
key_password: key_password
|
30
|
+
apksigner: apksigner,
|
31
|
+
apksigner_args: apksigner_args
|
35
32
|
})
|
36
33
|
end
|
37
34
|
|
@@ -41,17 +38,13 @@ module Fastlane
|
|
41
38
|
mask_keys: %i[keystore_password key_password]
|
42
39
|
)
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
UI.verbose "Packaging channel apk ..."
|
52
|
-
Helper::AndroidChannelsHelper.write_apk_with_channels(output_path, signed_file.path, channels)
|
53
|
-
signed_file.unlink
|
54
|
-
end
|
41
|
+
Helper::AndroidChannelsHelper.packing_apk(apk_file, channels, output_path, {
|
42
|
+
apksigner: apksigner,
|
43
|
+
apksigner_args: apksigner_args,
|
44
|
+
prefix: params[:channel_filename_prefix],
|
45
|
+
suffix: params[:channel_filename_suffix],
|
46
|
+
verify: params[:verify]
|
47
|
+
})
|
55
48
|
|
56
49
|
total = Dir["#{output_path}/*"].size
|
57
50
|
UI.success "Packaged done, total: #{total} apk file(s)."
|
@@ -60,7 +53,11 @@ module Fastlane
|
|
60
53
|
end
|
61
54
|
|
62
55
|
def self.description
|
63
|
-
"Package
|
56
|
+
"Package apk file with channels"
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.details
|
60
|
+
"Write empty file to META-INF with channel in general way"
|
64
61
|
end
|
65
62
|
|
66
63
|
def self.authors
|
@@ -71,9 +68,6 @@ module Fastlane
|
|
71
68
|
'The output of signed apk path'
|
72
69
|
end
|
73
70
|
|
74
|
-
def self.details
|
75
|
-
"Apply for QYER mobile team for now"
|
76
|
-
end
|
77
71
|
|
78
72
|
def self.available_options
|
79
73
|
[
|
@@ -86,13 +80,29 @@ module Fastlane
|
|
86
80
|
FastlaneCore::ConfigItem.new(key: :channels,
|
87
81
|
env_name: "ANDROID_CHANNELS_CHANNELS",
|
88
82
|
description: "The key password of keystore",
|
89
|
-
optional:
|
83
|
+
optional: true,
|
84
|
+
default_value: [],
|
90
85
|
type: Array),
|
86
|
+
FastlaneCore::ConfigItem.new(key: :channel_file,
|
87
|
+
env_name: "ANDROID_CHANNELS_CHANNEL_FILE",
|
88
|
+
description: "The path of channel file, accepts json, yaml and plain text file (split with space, comma and newline)",
|
89
|
+
optional: true,
|
90
|
+
type: String),
|
91
|
+
FastlaneCore::ConfigItem.new(key: :channel_filename_prefix,
|
92
|
+
env_name: "ANDROID_CHANNELS_CHANNEL_FILENAME_PREFIX",
|
93
|
+
description: "The prefix of empty channel file to write to METE-INF folder",
|
94
|
+
optional: true,
|
95
|
+
type: String),
|
96
|
+
FastlaneCore::ConfigItem.new(key: :channel_filename_suffix,
|
97
|
+
env_name: "ANDROID_CHANNELS_CHANNEL_FILENAME_SUFFIX",
|
98
|
+
description: "The suffix of empty channel file to write to METE-INF folder",
|
99
|
+
optional: true,
|
100
|
+
type: String),
|
91
101
|
FastlaneCore::ConfigItem.new(key: :output_path,
|
92
102
|
env_name: "ANDROID_CHANNELS_OUTPUT_PATH",
|
93
|
-
description: "The output path of
|
103
|
+
description: "The output path of channel apk files",
|
94
104
|
optional: true,
|
95
|
-
default_value: "
|
105
|
+
default_value: "channel_apks",
|
96
106
|
type: String),
|
97
107
|
FastlaneCore::ConfigItem.new(key: :android_sdk_path,
|
98
108
|
env_name: "ANDROID_CHANNELS_ANDROID_SDK_PATH",
|
@@ -126,6 +136,17 @@ module Fastlane
|
|
126
136
|
description: "The key password of keystore",
|
127
137
|
optional: true,
|
128
138
|
type: String),
|
139
|
+
FastlaneCore::ConfigItem.new(key: :apksigner_extra_args,
|
140
|
+
env_name: "ANDROID_CHANNELS_APKSIGNER_EXTRA_ARGS",
|
141
|
+
description: "The extra arguments of apksigner command",
|
142
|
+
optional: true,
|
143
|
+
type: String),
|
144
|
+
FastlaneCore::ConfigItem.new(key: :verify,
|
145
|
+
env_name: "ANDROID_CHANNELS_VERIFY",
|
146
|
+
description: "Do or not verify signed apk file",
|
147
|
+
optional: true,
|
148
|
+
default_value: false,
|
149
|
+
type: Boolean),
|
129
150
|
FastlaneCore::ConfigItem.new(key: :clean,
|
130
151
|
env_name: "ANDROID_CHANNELS_CLEAN",
|
131
152
|
description: "Should the signed files to be clean before signing it?",
|
@@ -146,17 +167,25 @@ module Fastlane
|
|
146
167
|
def self.example_code
|
147
168
|
[
|
148
169
|
'android_channels(
|
149
|
-
apk_file: "app
|
170
|
+
apk_file: "app.apk",
|
150
171
|
channels: ["xiaomi", "huawei", "qq"],
|
151
172
|
output_path: "/tmp/output_path",
|
173
|
+
verify: true
|
152
174
|
)',
|
153
175
|
'android_channels(
|
154
176
|
apk_file: "app-unsigned.apk",
|
155
|
-
signed: false,
|
156
177
|
channels: ["xiaomi", "huawei", "qq"],
|
157
178
|
keystore: "release.keystore",
|
158
179
|
keystore_password: "p@ssword",
|
159
180
|
clean: true
|
181
|
+
)',
|
182
|
+
'android_channels(
|
183
|
+
apk_file: "app-unsigned.apk",
|
184
|
+
channels: ["xiaomi", "huawei", "qq"],
|
185
|
+
channel_filename_prefix: "channel_",
|
186
|
+
keystore: "release.keystore",
|
187
|
+
keystore_extra_args: "--ks-pass env:app.env --key-alias app",
|
188
|
+
clean: true
|
160
189
|
)'
|
161
190
|
]
|
162
191
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'fastlane_core/ui/ui'
|
2
2
|
require 'shellwords'
|
3
3
|
require 'tempfile'
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
4
6
|
require 'zip'
|
5
7
|
|
6
8
|
module Fastlane
|
@@ -8,51 +10,64 @@ module Fastlane
|
|
8
10
|
|
9
11
|
module Helper
|
10
12
|
module AndroidChannelsHelper
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
signed_file
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.write_apk_with_channels(output_path, apk, channels)
|
13
|
+
APKSIGNER_COMMAND_KEYS = {
|
14
|
+
keystore: "--ks",
|
15
|
+
keystore_password: "--ks-pass",
|
16
|
+
key_alias: "--ks-key-alias",
|
17
|
+
key_password: "--key-pass",
|
18
|
+
}
|
19
|
+
|
20
|
+
# 签名接收的各渠道包
|
21
|
+
def self.packing_apk(apk_file, channels, output_path, options = {})
|
25
22
|
Zip.warn_invalid_date = false
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
|
24
|
+
UI.message "Packaing apk ..."
|
25
|
+
Tempfile.open("android_channel_name_file") do |empty_channel_file|
|
26
|
+
channels.each do |channel_name|
|
27
|
+
signed_and_write_channel_to_apk(apk_file, channel_name, output_path, empty_channel_file.path, options)
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
32
|
+
# 写入渠道并签名 apk 文件
|
33
|
+
def self.signed_and_write_channel_to_apk(apk_file, channel_name, output_path, write_file, options)
|
34
|
+
output_file = File.join(output_path, "#{channel_name}.apk")
|
35
|
+
Action.sh("cp #{apk_file} #{output_file}", print_command: false)
|
36
36
|
|
37
|
+
channel_filename = [options[:prefix], channel_name, options[:suffix]].compact.join("")
|
38
|
+
UI.verbose "Writing 'META-INF/#{channel_filename}' file to #{output_file}"
|
37
39
|
Zip::File.open(output_file, Zip::File::CREATE) do |zip_file|
|
38
|
-
zip_file.add("META-INF
|
40
|
+
zip_file.add("META-INF/#{channel_filename}", write_file)
|
41
|
+
end
|
42
|
+
|
43
|
+
UI.verbose "Signing ..."
|
44
|
+
sign_apk(output_file, options)
|
45
|
+
|
46
|
+
if options[:verify] && !verify_apk(output_file, options[:apksigner])
|
47
|
+
UI.build_failure! "Verify failure apk file: #{output_file}"
|
39
48
|
end
|
40
49
|
rescue Zip::EntryExistsError => ex
|
41
50
|
UI.build_failure!([ex.message].concat(ex.backtrace).join("\n"))
|
42
51
|
end
|
43
52
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
53
|
+
# 签名 apk 文件
|
54
|
+
def self.sign_apk(apk_file, options = {})
|
55
|
+
command = "#{options[:apksigner].shellescape} sign #{options[:apksigner_args]} #{apk_file}"
|
56
|
+
Action.sh(command, print_command: false, print_command_output: verbose?)
|
48
57
|
end
|
49
58
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
59
|
+
# 验证 apk 签名是否正确
|
60
|
+
def self.verify_apk(apk_file, apksigner)
|
61
|
+
command_args = [apksigner.shellescape, "verify"]
|
62
|
+
command_args << "--verbose" if verbose?
|
63
|
+
command_args << apk_file
|
64
|
+
|
65
|
+
UI.verbose "Verifing ..."
|
66
|
+
result = Action.sh(command_args.join(" "), print_command: false, print_command_output: verbose?)
|
67
|
+
!result.include?("DOES NOT VERIFY")
|
54
68
|
end
|
55
69
|
|
70
|
+
# 验证 apk 是否签名
|
56
71
|
def self.is_signed?(apk_file)
|
57
72
|
Zip.warn_invalid_date = false
|
58
73
|
Zip::File.open(apk_file, Zip::File::CREATE) do |zip_file|
|
@@ -71,22 +86,55 @@ module Fastlane
|
|
71
86
|
false
|
72
87
|
end
|
73
88
|
|
74
|
-
def self.
|
75
|
-
|
76
|
-
|
77
|
-
|
89
|
+
def self.verbose?
|
90
|
+
FastlaneCore::Globals.verbose?
|
91
|
+
end
|
92
|
+
|
93
|
+
# 解析并合并 apksigner 的参数, 额外的参数会覆盖之前设置过的值
|
94
|
+
def self.apksigner_args(params)
|
95
|
+
command = {}
|
96
|
+
params.all_keys.each do |key|
|
97
|
+
if APKSIGNER_COMMAND_KEYS.has_key?(key)
|
98
|
+
if command_value = params[key]
|
99
|
+
command_key = APKSIGNER_COMMAND_KEYS[key].to_s
|
100
|
+
command_value = "pass:#{command_value}" if command_key.end_with?("pass")
|
101
|
+
command[command_key] = command_value.shellescape
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
if extra_args = params[:apksigner_extra_args]
|
107
|
+
extra_args.split(" ").each_slice(2) do |part|
|
108
|
+
key = part[0].to_s.strip.shellescape
|
109
|
+
if value = part[1]
|
110
|
+
command[key.to_s] = value.strip.shellescape
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
command.flatten.join(" ")
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.determine_apk_file!(params)
|
119
|
+
apk_file = find_file(params[:apk_file])
|
120
|
+
UI.user_error!("Not found apk file: #{params[:apk_file]}") unless apk_file
|
121
|
+
apk_file
|
78
122
|
end
|
79
123
|
|
80
124
|
def self.determine_channels!(params)
|
81
125
|
channels = params[:channels]
|
126
|
+
if (file = params[:channel_file]) && File.exist?(file)
|
127
|
+
channels.concat(load_channel_file(file))
|
128
|
+
end
|
129
|
+
|
82
130
|
UI.user_error!("Empty channels") if channels.size.zero?
|
83
|
-
channels
|
131
|
+
channels.map{|n| n.strip}.uniq
|
84
132
|
end
|
85
133
|
|
86
|
-
def self.
|
87
|
-
|
88
|
-
UI.user_error!("
|
89
|
-
|
134
|
+
def self.determine_keystore!(params)
|
135
|
+
keystore = find_file(params[:keystore])
|
136
|
+
UI.user_error!("Not found keystore file: #{params[:keystore]}") unless keystore
|
137
|
+
keystore
|
90
138
|
end
|
91
139
|
|
92
140
|
def self.determine_output_path!(params)
|
@@ -122,6 +170,19 @@ module Fastlane
|
|
122
170
|
nil
|
123
171
|
end
|
124
172
|
|
173
|
+
def self.load_channel_file(file)
|
174
|
+
content = File.read(file)
|
175
|
+
case File.extname(file)
|
176
|
+
when ".json"
|
177
|
+
JSON.load(content).to_a
|
178
|
+
when ".yaml", ".yml"
|
179
|
+
YAML.load(content).to_a
|
180
|
+
else
|
181
|
+
# 安装纯文本解析
|
182
|
+
content.gsub(/(\s|\n)+/, ",").split(",").select {|n| n && !n.empty?}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
125
186
|
def self.build_tools_path(android_sdk_path, version = nil)
|
126
187
|
build_tools_path = File.join(android_sdk_path, "build-tools")
|
127
188
|
unless version
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-android_channels
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- icyleaf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-10-
|
11
|
+
date: 2018-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|
@@ -182,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
182
182
|
version: '0'
|
183
183
|
requirements: []
|
184
184
|
rubyforge_project:
|
185
|
-
rubygems_version: 2.7.
|
185
|
+
rubygems_version: 2.7.7
|
186
186
|
signing_key:
|
187
187
|
specification_version: 4
|
188
188
|
summary: Package unsign apk with channels
|