fastlane-plugin-android_channels 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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