fastlane-plugin-schindler 0.1.0 → 1.0.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
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 807e8efa8676d7f4ae9e446de84742e1c23b6eae68e40dc36ef5a8f91987f7bc
|
4
|
+
data.tar.gz: 4aea4f8595f23b7ab1873c1d99efaf3ada9580cd05e1522d16fbc8429ef7b03a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36aad8df14f77cc5139c8efbd2b9e141bd8589698e32056c745074ed26fa71ce36d9937c5261512c3924e6979eca5fee22333ac89736bdd3b85ea6705da7c8c6
|
7
|
+
data.tar.gz: 0baafb8326836c3900c61a269d79b55d1260f022dfec39c652b34a892ab1e4cadaa3dccfd798d0ac1328258d9ebc10f89e56fe25087a289b1202ae25da03ac07
|
data/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2022
|
3
|
+
Copyright (c) 2022 Xjkstar <Xjk_001@163.com>
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
[](https://rubygems.org/gems/fastlane-plugin-schindler)
|
4
4
|
|
5
|
+
|
6
|
+
## About Schindler
|
7
|
+
|
8
|
+
Schindler is a TestFlight automatic processing tool, which is used to maintain the number of TestFlight quota, eliminate useless testers, and improve the external gray effect of iOS.
|
9
|
+
|
10
|
+
|
5
11
|
## Getting Started
|
6
12
|
|
7
13
|
This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To get started with `fastlane-plugin-schindler`, add it to your project by running:
|
@@ -10,17 +16,45 @@ This project is a [_fastlane_](https://github.com/fastlane/fastlane) plugin. To
|
|
10
16
|
fastlane add_plugin schindler
|
11
17
|
```
|
12
18
|
|
13
|
-
##
|
19
|
+
## RubyGems
|
14
20
|
|
15
|
-
|
21
|
+
[fastlane-plugin-schindler](https://rubygems.org/gems/fastlane-plugin-schindler)
|
16
22
|
|
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.
|
18
23
|
|
19
24
|
## Example
|
25
|
+
### 1. Fastlane Ready
|
26
|
+
```bash
|
27
|
+
# install fastlane
|
28
|
+
gem install fastlane
|
29
|
+
# create a workspace
|
30
|
+
fastlane init
|
31
|
+
# add puglin
|
32
|
+
fastlane add_plugin schindler
|
33
|
+
```
|
34
|
+
|
35
|
+
### 2. Edit Fastfile
|
36
|
+
After Init succeeds, the fastlane folder will be generated in the current directory.
|
37
|
+
|
38
|
+
Edit ./fastlane/Fastfile,for example:
|
39
|
+
```ruby
|
40
|
+
# A sample Fastfile
|
41
|
+
lane :delete do
|
42
|
+
schindler(
|
43
|
+
filter_type: "7", # Optional, '1'-Not installed, '2'-Expired, '4'-Unused, '7'-All(1 | 2 | 4), default 7
|
44
|
+
auto_confirm: "auto", # Optional, 'auto'-skip, default no. Skip the second confirmation, or wait for user confirmation before deleting after scanning
|
45
|
+
user_id: "xjk_001@163.com", # Your AppID for login App Store Connect
|
46
|
+
user_password: "********", # AppID password, support App private password
|
47
|
+
ios_app_id: "11112222" # The ID of the app in the Apple Store
|
48
|
+
)
|
49
|
+
end
|
20
50
|
|
21
|
-
|
51
|
+
```
|
22
52
|
|
23
|
-
|
53
|
+
### 3. Execute
|
54
|
+
```bash
|
55
|
+
fastlane delete
|
56
|
+
```
|
57
|
+

|
24
58
|
|
25
59
|
## Run tests for this plugin
|
26
60
|
|
@@ -11,7 +11,7 @@ module Fastlane
|
|
11
11
|
Filter_UnUse = 1 << 2
|
12
12
|
|
13
13
|
def self.run(params)
|
14
|
-
UI.message(
|
14
|
+
UI.message('The schindler plugin is working!')
|
15
15
|
|
16
16
|
ARGV.clear
|
17
17
|
|
@@ -23,93 +23,115 @@ module Fastlane
|
|
23
23
|
puts "================================\n新建名单,初始化参数信息:\n筛选类型(位运算):#{filter_type} (#{Filter_Uninstall}-未安装,#{Filter_Expire}-已过期,#{Filter_UnUse}-未使用)\n跳过二次确认:#{auto_confirm}\n账号:#{user_id}\n密码:******\n应用ID:#{ios_app_id}\n================================"
|
24
24
|
|
25
25
|
# 先扫描未安装、已过期
|
26
|
-
if (
|
27
|
-
add_process_uninstall_expired(filter_type: filter_type, auto_confirm: auto_confirm, ios_app_id: ios_app_id,
|
26
|
+
if (filter_type & Filter_Uninstall > 0) || (filter_type & Filter_Expire > 0)
|
27
|
+
add_process_uninstall_expired(filter_type: filter_type, auto_confirm: auto_confirm, ios_app_id: ios_app_id,
|
28
|
+
user_id: user_id, user_password: user_password)
|
28
29
|
end
|
29
30
|
# 再扫描未使用,因为未使用需要全量查询,耗时1小时起步,容易超时
|
30
|
-
if
|
31
|
-
add_process_unused(auto_confirm: auto_confirm, ios_app_id: ios_app_id, user_id: user_id,
|
31
|
+
if filter_type & Filter_UnUse > 0
|
32
|
+
add_process_unused(auto_confirm: auto_confirm, ios_app_id: ios_app_id, user_id: user_id,
|
33
|
+
user_password: user_password)
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
def self.exec_process
|
37
|
+
def self.exec_process(testers, filter_type, auto_confirm)
|
36
38
|
return [] if testers.nil?
|
37
39
|
|
38
|
-
ids =
|
40
|
+
ids = []
|
39
41
|
# 90天前的安装
|
40
42
|
currentDate = DateTime.now.to_time
|
41
|
-
expiredtime =(currentDate - 60*60*24*90).to_i
|
43
|
+
expiredtime = (currentDate - 60 * 60 * 24 * 90).to_i
|
42
44
|
# 7天内更新的,不统计打开次数
|
43
|
-
sevendaytime = (currentDate - 60*60*24*7).to_i
|
45
|
+
sevendaytime = (currentDate - 60 * 60 * 24 * 7).to_i
|
44
46
|
|
45
47
|
testers.each do |tester|
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
puts "测试员信息:#{tester.to_json}"
|
49
|
+
|
50
|
+
unless tester.respond_to?(:beta_tester_metrics)
|
49
51
|
puts "\n\n~~~~~~~~~~ 异常跳过:不合法的BetaTester数据 ~~~~~~~~~~"
|
50
52
|
next
|
51
53
|
end
|
52
|
-
|
53
|
-
id = tester.id.to_s
|
54
|
+
|
55
|
+
id = tester.id.to_s
|
54
56
|
metric = (tester.beta_tester_metrics || []).first
|
55
57
|
tester_state = metric.beta_tester_state
|
56
|
-
puts "测试员:#{tester.id
|
57
|
-
|
58
|
+
puts "测试员:#{tester.id} 状态-#{tester_state}"
|
59
|
+
|
58
60
|
if metric.installed?
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
# 兼容没有毫秒的数据,苹果也不标准啊!
|
63
|
-
if modified_date_string.length == 29
|
64
|
-
modified_date = DateTime.strptime(modified_date_string, '%Y-%m-%dT%H:%M:%S.%L%z').to_time
|
65
|
-
elsif modified_date_string.length == 25
|
66
|
-
modified_date = DateTime.strptime(modified_date_string, '%Y-%m-%dT%H:%M:%S%z').to_time
|
67
|
-
else
|
68
|
-
puts "不合法的lastModifiedDate: #{modified_date_string}"
|
69
|
-
next
|
70
|
-
end
|
71
|
-
|
61
|
+
modified_date = string2date(metric.last_modified_date)
|
62
|
+
next if modified_date.nil?
|
63
|
+
|
72
64
|
last_date = modified_date.getlocal.to_i
|
73
65
|
puts "最后更新时间:#{modified_date}"
|
74
66
|
# 在sevenDaySessionCount支持前,暂时用session_count + lastModifiedDate
|
75
67
|
count = metric.session_count.to_i
|
76
68
|
|
77
|
-
if (
|
69
|
+
if (filter_type & Filter_Expire) > 0 && last_date < expiredtime
|
78
70
|
# 过期
|
79
|
-
ids << id
|
71
|
+
ids << id
|
80
72
|
|
81
|
-
puts
|
82
|
-
elsif (
|
73
|
+
puts '===== 已过期 +1 ====='
|
74
|
+
elsif (filter_type & Filter_UnUse) > 0 && count < 1 && last_date < sevendaytime
|
83
75
|
# 超过7天未使用
|
84
|
-
ids << id
|
76
|
+
ids << id
|
85
77
|
|
86
|
-
puts
|
78
|
+
puts '===== 未使用 +1 ====='
|
87
79
|
end
|
88
|
-
elsif
|
89
|
-
ids << id
|
80
|
+
elsif filter_type & Filter_Uninstall > 0
|
81
|
+
ids << id
|
90
82
|
|
91
|
-
puts
|
83
|
+
puts '===== 未安装 +1 ====='
|
92
84
|
end
|
93
85
|
end
|
94
|
-
|
86
|
+
|
95
87
|
puts "\n测试员筛选完成\n================================"
|
96
88
|
if ids.size < 1
|
97
89
|
puts "当前规则下,测试员已经删完干净了\n================================"
|
98
90
|
return []
|
99
91
|
end
|
100
|
-
|
101
|
-
|
92
|
+
|
93
|
+
unless auto_confirm
|
102
94
|
puts "删除TestFlight测试员:#{ids.size}个? (Y/n)"
|
103
|
-
if
|
95
|
+
if gets.chomp != 'Y'
|
104
96
|
puts "Cancel,任务取消,此次任务结束\n================================"
|
105
97
|
return []
|
106
98
|
end
|
107
99
|
end
|
108
100
|
|
109
|
-
|
101
|
+
ids
|
110
102
|
end
|
111
103
|
|
112
|
-
def self.
|
104
|
+
def self.string2date(origin_date_string)
|
105
|
+
modified_date_string = origin_date_string
|
106
|
+
# 在removeAfterDate支持前,暂时用lastModifiedDate. "last_modified_date":"2022-09-02T07:05:44.414-07:00"
|
107
|
+
modified_date = nil
|
108
|
+
# 为兼容没有毫秒的数据,统一干掉毫秒,苹果也不标准啊!
|
109
|
+
date_string_split = modified_date_string.split('.')
|
110
|
+
if date_string_split.size == 2
|
111
|
+
parse_string = date_string_split[1]
|
112
|
+
parse_string_split = parse_string.split('-')
|
113
|
+
if parse_string_split.size > 1
|
114
|
+
parse_string = '-' + parse_string_split[1]
|
115
|
+
else
|
116
|
+
puts "不合法的lastModifiedDate: #{origin_date_string}"
|
117
|
+
return modified_date
|
118
|
+
end
|
119
|
+
|
120
|
+
modified_date_string = date_string_split[0]
|
121
|
+
modified_date_string.concat(parse_string)
|
122
|
+
end
|
123
|
+
|
124
|
+
if modified_date_string.length == 25
|
125
|
+
modified_date = DateTime.strptime(modified_date_string, '%Y-%m-%dT%H:%M:%S%z').to_time
|
126
|
+
else
|
127
|
+
puts "不合法的lastModifiedDate: #{origin_date_string}"
|
128
|
+
return modified_date
|
129
|
+
end
|
130
|
+
|
131
|
+
modified_date
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.add_process_unused(auto_confirm: false, ios_app_id: nil, user_id: nil, user_password: nil)
|
113
135
|
# 查询全量数据,但容易超时,未使用的用户只能全量查询
|
114
136
|
puts "#{DateTime.now.to_time} 开始获取App数据……"
|
115
137
|
app = Spaceship::ConnectAPI::App.get(app_id: ios_app_id)
|
@@ -117,40 +139,41 @@ module Fastlane
|
|
117
139
|
puts "#{DateTime.now.to_time} 开始获取测试人员列表,带薪喝茶时间……"
|
118
140
|
testers = nil
|
119
141
|
if app
|
120
|
-
puts
|
121
|
-
testers = app.get_beta_testers(filter: {apps: ios_app_id, isDeleted: false}, includes:
|
142
|
+
puts 'App get_beta_testers'
|
143
|
+
testers = app.get_beta_testers(filter: { apps: ios_app_id, isDeleted: false }, includes: 'betaTesterMetrics',
|
144
|
+
sort: 'betaTesterMetrics.betaTesterState', limit: 20)
|
122
145
|
else
|
123
|
-
puts
|
124
|
-
testers = Spaceship::ConnectAPI::BetaTester.all(filter: {apps: ios_app_id, isDeleted: false},
|
146
|
+
puts 'Spaceship::ConnectAPI::BetaTester'
|
147
|
+
testers = Spaceship::ConnectAPI::BetaTester.all(filter: { apps: ios_app_id, isDeleted: false },
|
148
|
+
includes: 'betaTesterMetrics', sort: 'betaTesterMetrics.betaTesterState', limit: 20)
|
125
149
|
end
|
126
150
|
puts "#{DateTime.now.to_time} 获取测试人员列表成功,共#{testers.count}个"
|
127
151
|
|
128
152
|
ids = exec_process(testers, Filter_UnUse, auto_confirm)
|
129
|
-
if
|
130
|
-
return
|
131
|
-
end
|
153
|
+
return if ids.size < 1
|
132
154
|
|
133
155
|
client = Spaceship::ConnectAPI::Client.login(user_id, user_password)
|
134
|
-
puts
|
156
|
+
puts '登录成功'
|
135
157
|
client.delete_beta_testers_from_app(beta_tester_ids: ids, app_id: ios_app_id)
|
136
158
|
puts "Success,删除#{ids.size}个测试人员成功\n================================"
|
137
159
|
end
|
138
160
|
|
139
|
-
def self.add_process_uninstall_expired
|
161
|
+
def self.add_process_uninstall_expired(filter_type: (Filter_Uninstall | Filter_Expire), auto_confirm: false, ios_app_id: nil, user_id: nil, user_password: nil)
|
140
162
|
# 每次查询Top 50,直到没有符合的记录,适合按状态排序后的扫描
|
141
163
|
client = Spaceship::ConnectAPI::Client.login(user_id, user_password)
|
142
|
-
puts
|
164
|
+
puts '登录成功'
|
143
165
|
|
144
|
-
while true
|
166
|
+
while true
|
145
167
|
puts "#{DateTime.now.to_time} 开始获取测试人员列表,带薪喝茶时间……"
|
146
|
-
testers = client.get_beta_testers(filter: {apps: ios_app_id, isDeleted: false},
|
168
|
+
testers = client.get_beta_testers(filter: { apps: ios_app_id, isDeleted: false },
|
169
|
+
includes: 'betaTesterMetrics', sort: 'betaTesterMetrics.betaTesterState')
|
147
170
|
|
148
171
|
puts "#{DateTime.now.to_time} 获取测试人员列表成功,共#{testers.count}个"
|
149
172
|
|
150
173
|
ids = exec_process(testers, filter_type, auto_confirm)
|
151
174
|
if ids.size < 1
|
152
|
-
|
153
|
-
|
175
|
+
puts "未安装 or 已过期 测试员清除完毕\n================================"
|
176
|
+
return
|
154
177
|
end
|
155
178
|
|
156
179
|
client.delete_beta_testers_from_app(beta_tester_ids: ids, app_id: ios_app_id)
|
@@ -163,11 +186,11 @@ module Fastlane
|
|
163
186
|
#####################################################
|
164
187
|
|
165
188
|
def self.description
|
166
|
-
|
189
|
+
'Schindler is a TestFlight tool for release useless quota.'
|
167
190
|
end
|
168
191
|
|
169
192
|
def self.authors
|
170
|
-
[
|
193
|
+
['xjk_001@163.com']
|
171
194
|
end
|
172
195
|
|
173
196
|
def self.return_value
|
@@ -176,38 +199,38 @@ module Fastlane
|
|
176
199
|
|
177
200
|
def self.details
|
178
201
|
# Optional:
|
179
|
-
|
202
|
+
'Schindler is a TestFlight automatic processing tool, which is used to maintain the number of TestFlight quota, eliminate useless testers, and improve the external gray effect of iOS.'
|
180
203
|
end
|
181
204
|
|
182
205
|
def self.available_options
|
183
206
|
[
|
184
207
|
FastlaneCore::ConfigItem.new(key: :filter_type,
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
208
|
+
env_name: 'SCHINDLER_FILTER_TYPE',
|
209
|
+
description: "'1'-Not installed, '2'-Expired, '4'-Unused, '7'-All(1 | 2 | 4), default 7",
|
210
|
+
optional: true,
|
211
|
+
default_value: '7',
|
212
|
+
type: String),
|
190
213
|
FastlaneCore::ConfigItem.new(key: :auto_confirm,
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
214
|
+
env_name: 'SCHINDLER_AUTO_CONFIRM',
|
215
|
+
description: "'auto'-skip, default no. Skip the second confirmation, or wait for user confirmation before deleting after scanning",
|
216
|
+
optional: true,
|
217
|
+
default_value: 'none',
|
218
|
+
type: String),
|
196
219
|
FastlaneCore::ConfigItem.new(key: :user_id,
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
220
|
+
env_name: 'SCHINDLER_USER_ID',
|
221
|
+
description: 'Your AppID for login Connect',
|
222
|
+
optional: false,
|
223
|
+
type: String),
|
201
224
|
FastlaneCore::ConfigItem.new(key: :user_password,
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
225
|
+
env_name: 'SCHINDLER_PASSWORD',
|
226
|
+
description: 'AppID password, support App private password',
|
227
|
+
optional: false,
|
228
|
+
type: String),
|
206
229
|
FastlaneCore::ConfigItem.new(key: :ios_app_id,
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
230
|
+
env_name: 'SCHINDLER_APP_ID',
|
231
|
+
description: 'The ID of the app in the Apple Store',
|
232
|
+
optional: false,
|
233
|
+
type: String)
|
211
234
|
]
|
212
235
|
end
|
213
236
|
|
@@ -215,7 +238,7 @@ module Fastlane
|
|
215
238
|
# Adjust this if your plugin only works for a particular platform (iOS vs. Android, for example)
|
216
239
|
# See: https://docs.fastlane.tools/advanced/#control-configuration-by-lane-and-by-platform
|
217
240
|
#
|
218
|
-
[
|
241
|
+
%i[ios mac].include?(platform)
|
219
242
|
# true
|
220
243
|
end
|
221
244
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'fastlane_core/ui/ui'
|
2
2
|
|
3
3
|
module Fastlane
|
4
|
-
UI = FastlaneCore::UI unless Fastlane.const_defined?(
|
4
|
+
UI = FastlaneCore::UI unless Fastlane.const_defined?('UI')
|
5
5
|
|
6
6
|
module Helper
|
7
7
|
class SchindlerHelper
|
@@ -9,7 +9,7 @@ module Fastlane
|
|
9
9
|
# as `Helper::SchindlerHelper.your_method`
|
10
10
|
#
|
11
11
|
def self.show_message
|
12
|
-
UI.message(
|
12
|
+
UI.message('Hello from the schindler plugin helper!')
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-schindler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- xjkstar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-09-
|
11
|
+
date: 2022-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|