ocean_package 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.idea/.gitignore +8 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/dictionaries/ocean.xml +3 -0
- data/.idea/inspectionProfiles/Project_Default.xml +6 -0
- data/.idea/misc.xml +7 -0
- data/.idea/modules.xml +8 -0
- data/.idea/ocean_package.iml +27 -0
- data/.idea/vcs.xml +6 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +40 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/oceanpackage +5 -0
- data/bin/setup +8 -0
- data/lib/ocean_package.rb +41 -0
- data/lib/ocean_package/command.rb +162 -0
- data/lib/ocean_package/config.rb +41 -0
- data/lib/ocean_package/constants.rb +14 -0
- data/lib/ocean_package/dingtalk.rb +34 -0
- data/lib/ocean_package/fir.rb +167 -0
- data/lib/ocean_package/ipa.rb +188 -0
- data/lib/ocean_package/logger.rb +22 -0
- data/lib/ocean_package/oss.rb +92 -0
- data/lib/ocean_package/package.rb +242 -0
- data/lib/ocean_package/version.rb +3 -0
- data/ocean_package.gemspec +43 -0
- metadata +117 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
module OceanPackage
|
3
|
+
|
4
|
+
class Config
|
5
|
+
|
6
|
+
attr_accessor :workspace_path
|
7
|
+
attr_accessor :scheme
|
8
|
+
attr_accessor :configuration
|
9
|
+
|
10
|
+
# attr_accessor :ding_enabled
|
11
|
+
# attr_accessor :ding_token
|
12
|
+
|
13
|
+
def initialize(params = [])
|
14
|
+
argv = CLAide::ARGV.new(params)
|
15
|
+
|
16
|
+
@workspace_path = argv.option("workspace-path", "")
|
17
|
+
puts "workspace_path: #{@workspace_path}"
|
18
|
+
|
19
|
+
@scheme = argv.option("scheme", "")
|
20
|
+
puts "scheme: #{@scheme}"
|
21
|
+
|
22
|
+
@configuration = argv.option("configuration", "")
|
23
|
+
puts "configuration: #{@configuration}"
|
24
|
+
|
25
|
+
# @ding_enabled = argv.flag?("ding-enabled", false)
|
26
|
+
# puts "ding_enabled: #{@ding_enabled}"
|
27
|
+
#
|
28
|
+
# @ding_token = argv.option("ding-token", "")
|
29
|
+
# puts "ding_token: #{@ding_token}"
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def valid
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
module OceanPackage
|
3
|
+
|
4
|
+
module Constants
|
5
|
+
|
6
|
+
# 有效的 CONFIGURATION 值
|
7
|
+
AVAILABLE_CONFIGURATIONS = %w(Debug Release)
|
8
|
+
DEFAULT_CONFIGURATION = 'Debug'
|
9
|
+
DEFAULT_COMPANY_NAME = 'YourCompanyName'
|
10
|
+
DEFAULT_ARCHIVE_PATH = '$HOME/Documents/Ipas/'
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
module OceanPackage
|
3
|
+
|
4
|
+
class DingTalk
|
5
|
+
require "dingbot"
|
6
|
+
|
7
|
+
attr_accessor :token
|
8
|
+
|
9
|
+
def initialize(token)
|
10
|
+
@token = token
|
11
|
+
configure_ding_bot
|
12
|
+
end
|
13
|
+
|
14
|
+
def configure_ding_bot
|
15
|
+
DingBot.configure do |config|
|
16
|
+
config.endpoint = 'https://oapi.dingtalk.com/robot/send' # API endpoint URL, default: ENV['DINGTALK_API_ENDPOINT'] or https://oapi.dingtalk.com/robot/send
|
17
|
+
config.access_token = @token # access token, default: ENV['DINGTALK_ACCESS_TOKEN']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# 发送 text 消息,有 @ 某人效果
|
22
|
+
def send_text_message(content = '', at_mobiles = [], is_at_all = false)
|
23
|
+
message = DingBot::Message::Text.new(content, at_mobiles, is_at_all)
|
24
|
+
DingBot.send_msg(message)
|
25
|
+
end
|
26
|
+
|
27
|
+
# 发送消息卡片,可以附带链接,图片
|
28
|
+
def send_card_message(title = '', text = '')
|
29
|
+
message = DingBot::Message::WholeActionCard.new(title, text)
|
30
|
+
DingBot.send_msg(message)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
|
2
|
+
module OceanPackage
|
3
|
+
class Fir
|
4
|
+
|
5
|
+
# token
|
6
|
+
attr_accessor :token
|
7
|
+
# 更新日志
|
8
|
+
attr_accessor :change_log
|
9
|
+
# ipa文件包路径
|
10
|
+
attr_accessor :ipa_file_path
|
11
|
+
# log文件路径
|
12
|
+
attr_accessor :log_path
|
13
|
+
|
14
|
+
def initialize(token, change_log = "", ipa_file_path, log_path)
|
15
|
+
@token = token
|
16
|
+
@change_log = change_log
|
17
|
+
@ipa_file_path = ipa_file_path
|
18
|
+
@log_path = log_path
|
19
|
+
end
|
20
|
+
|
21
|
+
# 命令:查看 fir 信息
|
22
|
+
def info_cmd
|
23
|
+
'fir -v'
|
24
|
+
end
|
25
|
+
|
26
|
+
# 命令:fir 登录
|
27
|
+
def login_cmd
|
28
|
+
cmd = 'fir login'
|
29
|
+
cmd += ' -T ' + @token
|
30
|
+
|
31
|
+
Log.info("fir login command: #{cmd}")
|
32
|
+
|
33
|
+
cmd
|
34
|
+
end
|
35
|
+
|
36
|
+
# 命令:上传ipa文件到fir平台
|
37
|
+
def publish_cmd
|
38
|
+
cmd = 'fir publish'
|
39
|
+
cmd += ' ' + @ipa_file_path
|
40
|
+
cmd += ' -c ' + @change_log
|
41
|
+
cmd += ' -Q'
|
42
|
+
cmd += ' | tee ' + @log_path
|
43
|
+
|
44
|
+
Log.info("fir publish command: #{cmd}")
|
45
|
+
|
46
|
+
cmd
|
47
|
+
end
|
48
|
+
|
49
|
+
# 运行
|
50
|
+
def run
|
51
|
+
unless check
|
52
|
+
return
|
53
|
+
end
|
54
|
+
login
|
55
|
+
publish
|
56
|
+
end
|
57
|
+
|
58
|
+
# 校验
|
59
|
+
def check
|
60
|
+
|
61
|
+
token_value = "#{@token}"
|
62
|
+
if token_value.empty?
|
63
|
+
Log.error("fir token is empty, please check !!!")
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
|
67
|
+
ipa_file_path_value = "#{@ipa_file_path}"
|
68
|
+
if ipa_file_path_value.empty?
|
69
|
+
Log.error("ipa file path is empty, please check !!!")
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
|
73
|
+
return true
|
74
|
+
end
|
75
|
+
|
76
|
+
# 登录
|
77
|
+
def login
|
78
|
+
system(info_cmd)
|
79
|
+
res = system(login_cmd)
|
80
|
+
|
81
|
+
Log.info("fir login result: #{res}")
|
82
|
+
|
83
|
+
unless res == true
|
84
|
+
Log.error("fir login fail, please check !!!")
|
85
|
+
exit(1)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
# 上传
|
91
|
+
def publish
|
92
|
+
res = system(publish_cmd)
|
93
|
+
|
94
|
+
Log.info("fir publish result: #{res}")
|
95
|
+
|
96
|
+
unless res == true
|
97
|
+
Log.error("fir publish fail, please check !!!")
|
98
|
+
exit(1)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# 查找 release_id
|
103
|
+
def find_release_id
|
104
|
+
# 正则表达式匹配
|
105
|
+
pattern = /.*Release id is.*/
|
106
|
+
release_id = ''
|
107
|
+
File.open(@log_path, "r") do |f|
|
108
|
+
f.each_line do |line|
|
109
|
+
line_s = "#{line}"
|
110
|
+
if line_s =~ pattern
|
111
|
+
release_id = line_s.split(' ').last
|
112
|
+
break
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
Log.info("fir release id value: #{release_id}")
|
117
|
+
release_id
|
118
|
+
end
|
119
|
+
|
120
|
+
# 查找下载链接
|
121
|
+
def find_link
|
122
|
+
# 正则表达式匹配
|
123
|
+
pattern = /.*Published succeed:.*/
|
124
|
+
link = ''
|
125
|
+
File.open(@log_path, "r") do |f|
|
126
|
+
f.each_line do |line|
|
127
|
+
line_s = "#{line}"
|
128
|
+
if line_s =~ pattern
|
129
|
+
link = line_s.split(' ').last
|
130
|
+
break
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
Log.info("fir link value: #{link}")
|
135
|
+
link
|
136
|
+
end
|
137
|
+
|
138
|
+
# 构建完整的下载链接,有 release id
|
139
|
+
def whole_download_link
|
140
|
+
link = find_link
|
141
|
+
link += '?release_id='
|
142
|
+
link += find_release_id
|
143
|
+
|
144
|
+
Log.info("fir whole link value: #{link}")
|
145
|
+
|
146
|
+
link
|
147
|
+
end
|
148
|
+
|
149
|
+
# 查找下载二维码的文件路径
|
150
|
+
def find_qr_code_path
|
151
|
+
# 正则表达式匹配
|
152
|
+
pattern = /.*Local qrcode file:.*/
|
153
|
+
path = ''
|
154
|
+
File.open(@log_path, "r") do |f|
|
155
|
+
f.each_line do |line|
|
156
|
+
line_s = "#{line}"
|
157
|
+
if line_s =~ pattern
|
158
|
+
path = line_s.split(' ').last
|
159
|
+
break
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
Log.info("fir qr code path value: #{path}")
|
164
|
+
path
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
|
2
|
+
module OceanPackage
|
3
|
+
class Ipa
|
4
|
+
|
5
|
+
# ipa 文件路径
|
6
|
+
attr_accessor :path
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
# ipa 文件所在的文件夹路径
|
13
|
+
def ipa_dir_path
|
14
|
+
dir_path = File.dirname(@path)
|
15
|
+
|
16
|
+
Log.info("ipa dir path: #{dir_path}")
|
17
|
+
|
18
|
+
dir_path
|
19
|
+
end
|
20
|
+
|
21
|
+
# ipa 文件名
|
22
|
+
def ipa_name
|
23
|
+
name = File.basename(@path, ".*")
|
24
|
+
|
25
|
+
Log.info("ipa name: #{name}")
|
26
|
+
|
27
|
+
name
|
28
|
+
end
|
29
|
+
|
30
|
+
# ipa文件名称,包含后缀
|
31
|
+
def ipa_base_name
|
32
|
+
name = File.basename(@path)
|
33
|
+
|
34
|
+
Log.info(puts "ipa base name: #{name}")
|
35
|
+
|
36
|
+
name
|
37
|
+
end
|
38
|
+
|
39
|
+
# 临时目录路径
|
40
|
+
def ipa_tmp_dir_path
|
41
|
+
tmp_path = ipa_dir_path
|
42
|
+
tmp_path += '/' + ipa_name + '_tmp'
|
43
|
+
tmp_path
|
44
|
+
end
|
45
|
+
|
46
|
+
# 临时目录下的ipa文件
|
47
|
+
def tmp_ipa_file_path
|
48
|
+
tmp_path = ipa_tmp_dir_path + '/' + ipa_base_name
|
49
|
+
|
50
|
+
Log.info("tmp ipa file path: #{tmp_path}")
|
51
|
+
|
52
|
+
tmp_path
|
53
|
+
end
|
54
|
+
|
55
|
+
# 临时目录下的 zip 文件路径,把 ipa 后缀改为 zip 后缀
|
56
|
+
def tmp_ipa_zip_file_path
|
57
|
+
zip_file = ipa_tmp_dir_path + '/' + ipa_name + '.zip'
|
58
|
+
|
59
|
+
Log.info(puts "tmp ipa zip file path: #{zip_file}")
|
60
|
+
|
61
|
+
zip_file
|
62
|
+
end
|
63
|
+
|
64
|
+
# 创建临时文件夹
|
65
|
+
def make_tmp_dir
|
66
|
+
FileUtils.mkdir_p(ipa_tmp_dir_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
# 拷贝ipa文件到临时目录下
|
70
|
+
def cp_to_tmp_dir
|
71
|
+
FileUtils.cp(@path, ipa_tmp_dir_path)
|
72
|
+
end
|
73
|
+
|
74
|
+
# 冲命名ipa文件,ipa -> zip
|
75
|
+
def rename_tmp_ipa_file
|
76
|
+
ipa_file = tmp_ipa_file_path
|
77
|
+
new_ipa_file = tmp_ipa_zip_file_path
|
78
|
+
|
79
|
+
Log.info("rename ipa file #{ipa_file} to: #{new_ipa_file}")
|
80
|
+
|
81
|
+
File.rename(ipa_file, new_ipa_file)
|
82
|
+
end
|
83
|
+
|
84
|
+
# 解压缩 zip 文件
|
85
|
+
def unzip_ipa_file
|
86
|
+
cmd = 'unzip -o'
|
87
|
+
cmd += ' -d ' + ipa_tmp_dir_path
|
88
|
+
cmd += ' ' + tmp_ipa_zip_file_path
|
89
|
+
|
90
|
+
Log.info("unzip cmd: #{cmd}")
|
91
|
+
|
92
|
+
res = system(cmd)
|
93
|
+
end
|
94
|
+
|
95
|
+
# 查找 info.plist 文件
|
96
|
+
def find_info_plist_path
|
97
|
+
plist_dir = ipa_tmp_dir_path
|
98
|
+
plist_dir += '/Payload'
|
99
|
+
|
100
|
+
cmd = 'find ' + plist_dir
|
101
|
+
cmd += ' -maxdepth 2'
|
102
|
+
cmd += ' -name Info.plist'
|
103
|
+
|
104
|
+
Log.info("find info plist path cmd: #{cmd}")
|
105
|
+
|
106
|
+
res = %x(#{cmd})
|
107
|
+
# res = system(cmd)
|
108
|
+
|
109
|
+
Log.info("info plist path: #{res}")
|
110
|
+
|
111
|
+
res
|
112
|
+
end
|
113
|
+
|
114
|
+
def find_info_plist_dir
|
115
|
+
plist_path = find_info_plist_path
|
116
|
+
dir = File.dirname(plist_path)
|
117
|
+
|
118
|
+
# Log.info("find info plist dir: #{dir}")
|
119
|
+
|
120
|
+
dir
|
121
|
+
end
|
122
|
+
|
123
|
+
# 读取 info.plist 文件的值
|
124
|
+
def info_plist_value
|
125
|
+
@info ||= CFPropertyList.native_types(
|
126
|
+
CFPropertyList::List.new(file: File.join(find_info_plist_dir, 'Info.plist')).value)
|
127
|
+
|
128
|
+
# Log.info("info plist value: #{@info}")
|
129
|
+
|
130
|
+
@info
|
131
|
+
|
132
|
+
# 这种写法不行的,会有如下错误
|
133
|
+
# UnexpectedError: IOError: File /Users/ocean/Documents/myipas/ztoExpressClient_tmp/Payload/ztoExpressClient.app/Info.plist not readable!
|
134
|
+
# @info ||= CFPropertyList.native_types(
|
135
|
+
# CFPropertyList::List.new(file: find_info_plist_path).value)
|
136
|
+
#
|
137
|
+
# puts "info_plist_value: #{@info}"
|
138
|
+
# @info
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
# app 名称
|
143
|
+
def display_name
|
144
|
+
info_plist_value["CFBundleDisplayName"]
|
145
|
+
end
|
146
|
+
|
147
|
+
# bundle id
|
148
|
+
def bundle_identifier
|
149
|
+
info_plist_value["CFBundleIdentifier"]
|
150
|
+
end
|
151
|
+
|
152
|
+
# 版本
|
153
|
+
def version
|
154
|
+
info_plist_value["CFBundleShortVersionString"]
|
155
|
+
end
|
156
|
+
|
157
|
+
# build 版本
|
158
|
+
def build_version
|
159
|
+
info_plist_value["CFBundleVersion"]
|
160
|
+
end
|
161
|
+
|
162
|
+
def log_value
|
163
|
+
display_name_value = display_name
|
164
|
+
puts "CFBundleDisplayName: #{display_name_value}"
|
165
|
+
|
166
|
+
bundle_identifier_value = bundle_identifier
|
167
|
+
puts "CFBundleIdentifier: #{bundle_identifier_value}"
|
168
|
+
|
169
|
+
version_value = version
|
170
|
+
puts "CFBundleShortVersionString: #{version_value}"
|
171
|
+
|
172
|
+
build_version_value = build_version
|
173
|
+
puts "CFBundleVersion: #{build_version_value}"
|
174
|
+
end
|
175
|
+
|
176
|
+
# 执行相关操作
|
177
|
+
def run
|
178
|
+
make_tmp_dir
|
179
|
+
cp_to_tmp_dir
|
180
|
+
rename_tmp_ipa_file
|
181
|
+
unzip_ipa_file
|
182
|
+
info_plist_value
|
183
|
+
log_value
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module OceanPackage
|
3
|
+
module Log
|
4
|
+
|
5
|
+
def Log.info(msg)
|
6
|
+
logger = Logger.new(STDOUT)
|
7
|
+
logger.info(msg)
|
8
|
+
end
|
9
|
+
|
10
|
+
def Log.error(msg)
|
11
|
+
logger = Logger.new(STDOUT)
|
12
|
+
logger.error(msg)
|
13
|
+
end
|
14
|
+
|
15
|
+
# 分割线
|
16
|
+
def Log.divider
|
17
|
+
logger = Logger.new(STDOUT)
|
18
|
+
logger.info("=========")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|