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: 78a1d2d5d451df8a32ec9f6f6fe05a80a67c6615158b97dfa371ba5e18ef3d50
4
- data.tar.gz: '0908ad55264545f787a1f190155a480bff7dd71ba228b06c414a986b823cbb3c'
3
+ metadata.gz: e53b39ed711bb97fe656417eec498e5631621bdab69f93efcd8e32fad9b20151
4
+ data.tar.gz: 38082a02a372af9be715e6ef528362be2fd9b10a07add4c297837f71897d3194
5
5
  SHA512:
6
- metadata.gz: a2064057157aca710149bae18336e88ebf3070719eae6474f829383dbc6bd60f47f254bc05dccb6b13e9312511bdab11fb7c288abca9de80ad5547ef86d929ef
7
- data.tar.gz: d2a6405e66c6b538ec4402b6c06f5b9833bf1a5c226c6952178e2773f799dce7283f2529e4d7014e4a3935020a1955f80fce69360b45423ae34e490d065abb97
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
- **Note to author:** Add a more detailed description about this plugin here. If your plugin contains multiple actions, make sure to mention them here.
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
- apksigner = Helper::AndroidChannelsHelper.determine_apksigner!(params)
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
- keystore_password: keystore_password,
33
- key_alias: key_alias,
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
- if signed
45
- UI.verbose "Packaging channel apk ..."
46
- Helper::AndroidChannelsHelper.write_apk_with_channels(output_path, apk_file, channels)
47
- else
48
- UI.verbose "Signing apk ..."
49
- signed_file = Helper::AndroidChannelsHelper.sign_apk(apksigner, keystore, keystore_password, apk_file, key_alias, key_password)
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 unsign apk with channels"
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: false,
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 channels apk files",
103
+ description: "The output path of channel apk files",
94
104
  optional: true,
95
- default_value: "channels_apk",
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-signed.apk",
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
- def self.sign_apk(apksigner, keystore, keystore_password, apk_file, key_alias = nil, key_password = nil)
12
- signed_file = Tempfile.new([File.basename(apk_file.sub("unsigned", "signed")), File.extname(apk_file)])
13
- FileUtils.cp(apk_file, signed_file.path)
14
-
15
- command_args = [apksigner.shellescape, "sign", "--ks-pass", "pass:#{keystore_password}", "--ks", keystore.shellescape]
16
- command_args << "--ks-key-alias" << key_alias unless key_alias.to_s.empty?
17
- command_args << "--key-pass" << "pass:#{key_password}" unless key_password.to_s.empty?
18
- command_args << signed_file.path
19
- Action.sh(command_args.join(" "), print_command: FastlaneCore::Globals.verbose?)
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
- with_channel_file do |file|
27
- channels.each do |name|
28
- write_channel_to_apk(output_path, apk, name, file.path)
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
- def self.write_channel_to_apk(output_path, signed_file, name, channel_file)
34
- output_file = File.join(output_path, "#{name}.apk")
35
- FileUtils.cp(signed_file, output_file)
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/cztchannel_#{name}", channel_file)
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
- def self.with_channel_file(filename = "android_channel", &block)
45
- Tempfile.open(filename) do |file|
46
- yield file
47
- end
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
- def self.determine_apk_file!(params)
51
- apk_file = find_file(params[:apk_file])
52
- UI.user_error!("Not found apk file: #{params[:apk_file]}") unless apk_file
53
- apk_file
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.determine_keystore!(params)
75
- keystore = find_file(params[:keystore])
76
- UI.user_error!("Not found keystore file: #{params[:keystore]}") unless keystore
77
- keystore
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.determine_keystore_password!(params)
87
- password = params[:keystore_password]
88
- UI.user_error!("Missing keystore_password") unless password
89
- password
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
@@ -1,5 +1,5 @@
1
1
  module Fastlane
2
2
  module AndroidChannels
3
- VERSION = "0.2.0"
3
+ VERSION = "0.3.1"
4
4
  end
5
5
  end
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.2.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-25 00:00:00.000000000 Z
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.3
185
+ rubygems_version: 2.7.7
186
186
  signing_key:
187
187
  specification_version: 4
188
188
  summary: Package unsign apk with channels