fir-cli 2.0.9 → 2.0.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/Dockerfile +1 -1
- data/README.md +52 -23
- data/doc/publish.md +45 -22
- data/fir-cli.gemspec +8 -2
- data/lib/fir.rb +1 -0
- data/lib/fir/api.yml +1 -0
- data/lib/fir/cli.rb +2 -1
- data/lib/fir/util/dingtalk_helper.rb +67 -0
- data/lib/fir/util/feishu_helper.rb +96 -0
- data/lib/fir/util/publish.rb +14 -76
- data/lib/fir/util/third_notifier_module.rb +40 -0
- data/lib/fir/version.rb +1 -1
- data/test/test_helper.rb +1 -4
- metadata +27 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ce0252e1ffcd6e9a73ce64fa5359fa06ce77416c93ffd394c6829481956a878
|
4
|
+
data.tar.gz: 5e8443ef24cc7d39aded9bdc1044dd9d8a11452bb081e1023dc9719aed9df0c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19439af4cd18638db3838e157db517e3c2b92045d2c27e269c75ef9eda3dc98b0c79d1d776d72f05ceca466241faefdf8cd242b4a438090019f3420fa9645aa5
|
7
|
+
data.tar.gz: 4c45e66b869e58df20966aae25780612d2c7abd7c5a287547bbe68a09dc1c6773fe4316df00c421d66bd58a08eb87fec985d5aad34a2084d8990da23b5cd5482
|
data/.travis.yml
CHANGED
data/Dockerfile
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
✈ fir.im-cli
|
2
|
-
----
|
2
|
+
----
|
3
3
|
|
4
4
|
![Build Status Images](https://travis-ci.org/FIRHQ/fir-cli.svg)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/FIRHQ/fir-cli/badges/gpa.svg)](https://codeclimate.com/github/FIRHQ/fir-cli)
|
@@ -13,11 +13,15 @@ fir.im-cli 可以通过指令查看, 上传, iOS/Android 应用.
|
|
13
13
|
![fir-cli](http://7rf35s.com1.z0.glb.clouddn.com/fir-cli-new.gif)
|
14
14
|
|
15
15
|
# 重大提醒
|
16
|
-
- fir.im
|
17
|
-
|
18
|
-
当然还有种解决办法是 更新 `fir-cli` 至 `2.0.5`, 这个版本换到了备用域名
|
16
|
+
- fir.im 更换域名至 betaqr.com 后, 需要升级至 `fir-cli` >= `2.0.4` 有部分用户反馈 2.0.2 无法直接使用 `gem update fir-cli` 升级到 2.0.4, 则可以尝试卸载后重新安装, 即 `gem uninstall fir-cli` 后 `gem install fir-cli`
|
17
|
+
|
19
18
|
|
20
19
|
# 最近更新
|
20
|
+
- (2.0.14) 第三方通知加入了 app 类型, 第三方报错将忽略异常继续运行
|
21
|
+
- (2.0.13) 修正了企业微信通知的bug
|
22
|
+
- (2.0.12) 修复因为钉钉机器人不再支持base64导致无法显示二维码,另外开始支持钉钉加签方式的鉴权, 参数为 --dingtalk_secret
|
23
|
+
- (2.0.11) 兼容了 ruby 3.0
|
24
|
+
- (2.0.10) 飞书支持了 V2 版本的机器人推送
|
21
25
|
- (2.0.9) publish 支持了 企业微信通知 可以使用 --wxwork_access_token 或 --wxwork_webhook, 增加了回调超时时间至20秒
|
22
26
|
- (2.0.8) publish 支持 飞书通知, 可使用 `feishu_access_token` 和 `feishu_custom_message`, 详情见 `fir publish --help`
|
23
27
|
- (2.0.7) 修复了提示 token 错误的问题
|
@@ -47,24 +51,49 @@ fir.im-cli 可以通过指令查看, 上传, iOS/Android 应用.
|
|
47
51
|
|
48
52
|
## 热门问题
|
49
53
|
|
50
|
-
###
|
54
|
+
### 啥是 钉钉 / 企业微信 / 飞书 的 `access_token` ?
|
55
|
+
|
56
|
+
就是回调地址中的长得最像 access_token 的东西
|
57
|
+
|
58
|
+
```
|
59
|
+
钉钉: https://oapi.dingtalk.com/robot/send?access_token=xxxxx
|
60
|
+
就是 xxx 那部分
|
61
|
+
|
62
|
+
企业微信: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxx-xxxx-xxxx-xxxx-xxxxx
|
63
|
+
就是 xxxxx-xxxx-xxxx-xxxx-xxxxx 那部分
|
64
|
+
|
65
|
+
https://open.feishu.cn/open-apis/bot/hook/xxxxxxxxxxxxxxxxxxx
|
66
|
+
|
67
|
+
就是 xxxxxxxxxxxxxxxx 那部分
|
68
|
+
|
69
|
+
```
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
### 如何配合 jenkins 使用?
|
51
76
|
|
52
77
|
参见 blog [http://blog.betaqr.com/use-fir-cli-in-jenkins/](http://blog.betaqr.com/use-fir-cli-in-jenkins/)
|
53
78
|
|
54
|
-
|
79
|
+
这里有个快速检查脚本, 可以在 jenkins 中新建一个 脚本, 进行检查
|
55
80
|
|
56
|
-
|
81
|
+
```
|
82
|
+
#!/bin/bash --login
|
57
83
|
|
84
|
+
rvm list # 确保 rvm 正确安装, 如果直接通过系统安装ruby, 可以注释此行
|
85
|
+
ruby -v # 查看 ruby 的版本, 请确保大于 2.4.0
|
86
|
+
gem install fir-cli # 现场安装fir-cli , 如果安装过, 则会略过
|
87
|
+
fir -v # 查看 fir-cli 的版本
|
58
88
|
|
59
|
-
|
89
|
+
# 在这里执行 fir publish xxxxx
|
90
|
+
```
|
60
91
|
|
61
92
|
|
93
|
+
### 在 Circle CI, Travis CI 或 Github Actions 等境外服务上, 有概率超时, 能否解决?
|
62
94
|
|
63
|
-
|
95
|
+
2.0.3 版本 及其以上, 可以申请海外加速内测资格, 开启后可以使用海外加速上传 `--oversea_turbo`
|
64
96
|
|
65
|
-
1. 设置较长的超时时间 如 `FIR_TIMEOUT=300 fir publish xxxx`, 则可将超时时间设置为 300 秒.
|
66
|
-
2. 如果fir-cli 版本 >= 2.0.0, 也可在出现超时后, 使用 `--switch-to-qiniu` 来切换到我们的另外一条存储线路. 如 `fir publish xxxx.apk --switch-to-qiniu`, 多一条线路备选.
|
67
|
-
3. 如果确实对这方面服务要求很高, 则可购买我们的私有部署服务. 我们可以在 aws 上部署一套专供您使用, 具体情况可以与客服或者加我微信进行沟通.
|
68
97
|
|
69
98
|
更多细节请参考 [https://github.com/FIRHQ/fir-cli/issues/260](https://github.com/FIRHQ/fir-cli/issues/260)
|
70
99
|
|
@@ -93,13 +122,13 @@ fir-cli 提供对 aab 文件有限程度支持的上传与下载. 在使用 fir-
|
|
93
122
|
|
94
123
|
### 我想将 我上传的版本展示在下载的页面上
|
95
124
|
|
96
|
-
可以在 publish 的时候使用 --force_pin_history 这样 这个上传的版本即成为 "历史版本", 会在下载页面里一直显示. 当有新的版本上传后, 这个版本会作为 "历史版本" 在下载页面中展示.
|
125
|
+
可以在 publish 的时候使用 --force_pin_history 这样 这个上传的版本即成为 "历史版本", 会在下载页面里一直显示. 当有新的版本上传后, 这个版本会作为 "历史版本" 在下载页面中展示.
|
97
126
|
|
98
127
|
当版本设置为历史版本后, 用户可以直接下载指定的版本, 由于因成本原因, 一个 app 最多的 "历史版本" 为 30 个, 如果有用户有特殊需求, 可以与我们取得联系进行单独修改
|
99
128
|
|
100
129
|
当达到上限后, 如果继续标记 force_pin_history, 则历史版本的最老版本(以上传时间为准)会被移出历史版本列表
|
101
130
|
|
102
|
-
### 境外上传老出现 stream closed
|
131
|
+
### 境外上传老出现 stream closed
|
103
132
|
|
104
133
|
因为网络时延问题, 可传入环境变量 `FIR_TIMEOUT=xxx` 进行超时时间设置
|
105
134
|
|
@@ -114,22 +143,22 @@ fir-cli 提供对 aab 文件有限程度支持的上传与下载. 在使用 fir-
|
|
114
143
|
- [fir publish 发布应用到 fir.im](https://github.com/FIRHQ/fir-cli/blob/master/doc/publish.md)
|
115
144
|
- [fir upgrade 升级相关](https://github.com/FIRHQ/fir-cli/blob/master/doc/upgrade.md)
|
116
145
|
|
117
|
-
## Docker 运行 fir-cli
|
146
|
+
## Docker 运行 fir-cli
|
118
147
|
|
119
148
|
### 准备工作
|
120
149
|
1. 将自己需要的文件挂载到 docker 中, 之后即可直接运行
|
121
|
-
2. 将自己的 API_TOKEN 以环境变量的形式传入container
|
150
|
+
2. 将自己的 API_TOKEN 以环境变量的形式传入container
|
122
151
|
|
123
152
|
### 如何运行
|
124
153
|
|
125
|
-
假设 我需要上传桌面的 1.apk
|
154
|
+
假设 我需要上传桌面的 1.apk
|
126
155
|
|
127
156
|
```
|
128
|
-
docker run -e API_TOKEN=您的token -v 您的上传文件的目录的绝对路径:/tmp firhq/fir-cli:latest publish /tmp/你的文件
|
157
|
+
docker run -e API_TOKEN=您的token -v 您的上传文件的目录的绝对路径:/tmp firhq/fir-cli:latest publish /tmp/你的文件
|
129
158
|
|
130
159
|
# 如 `docker run -e API_TOKEN=xxxxxxxe -v /Users/atpking/Desktop:/tmp firhq/fir-cli:latest publish /tmp/1.apk`
|
131
160
|
|
132
|
-
# 实际含义是把我的桌面挂载到 docker 里的 /tmp 目录 之后上传 docker 文件里的 /tmp/1.apk
|
161
|
+
# 实际含义是把我的桌面挂载到 docker 里的 /tmp 目录 之后上传 docker 文件里的 /tmp/1.apk
|
133
162
|
# 也可以修改为其他目录
|
134
163
|
```
|
135
164
|
|
@@ -137,13 +166,13 @@ docker run -e API_TOKEN=您的token -v 您的上传文件的目录的绝对路
|
|
137
166
|
|
138
167
|
- 联系微信 `atpking`, 请注明 "fir-cli 交流"
|
139
168
|
|
140
|
-
- 使用 Github 的 [Issue](https://github.com/FIRHQ/fir-cli/issues)
|
169
|
+
- 使用 Github 的 [Issue](https://github.com/FIRHQ/fir-cli/issues)
|
141
170
|
|
142
|
-
## 特别感谢
|
171
|
+
## 特别感谢
|
143
172
|
|
144
173
|
- 感谢 sparkrico 提供修正的 https://github.com/sparkrico/ruby_apk 解决了 android 解析的问题
|
145
|
-
- 感谢 fabcz 同学对企业微信的通知的支持 https://github.com/FIRHQ/fir-cli/pull/277
|
174
|
+
- 感谢 fabcz 同学对企业微信的通知的支持 https://github.com/FIRHQ/fir-cli/pull/277
|
146
175
|
|
147
176
|
## 鼓励维护
|
148
177
|
|
149
|
-
|
178
|
+
hia~ hia~ hia~
|
data/doc/publish.md
CHANGED
@@ -3,33 +3,56 @@
|
|
3
3
|
`fir publish` 命令用于可以发布应用到 fir.im, 支持 ipa 和 apk 文件.
|
4
4
|
|
5
5
|
```sh
|
6
|
-
$ fir publish --help
|
7
6
|
Usage:
|
8
7
|
fir publish APP_FILE_PATH
|
9
8
|
|
10
9
|
Options:
|
11
|
-
-s, [--short=SHORT]
|
12
|
-
-c, [--changelog=CHANGELOG]
|
13
|
-
-Q, [--qrcode], [--no-qrcode]
|
14
|
-
[--
|
15
|
-
|
16
|
-
-
|
17
|
-
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
-
|
10
|
+
-s, [--short=SHORT] # 设置short
|
11
|
+
-c, [--changelog=CHANGELOG] # 设置更新内容, 可是文件地址也可直接是内容
|
12
|
+
-Q, [--qrcode], [--no-qrcode] # 生成二维码图片在当前目录
|
13
|
+
[--need-ansi-qrcode], [--no-need-ansi-qrcode] #
|
14
|
+
-R, [--need-release-id], [--no-need-release-id] # 在下载地址中包含具体版本(警告, 每个app 最多保留30个版本, 超过后会失效最老的版本)
|
15
|
+
-H, [--force-pin-history], [--no-force-pin-history] # 将版本留在下载页(即在新版本上传后, 下面仍然有旧版本的二维码可以引导)
|
16
|
+
-S, [--skip-update-icon], [--no-skip-update-icon] # 跳过更新图标
|
17
|
+
[--specify-icon-file=SPECIFY_ICON_FILE] # 指定更新图标
|
18
|
+
[--skip-fir-cli-feedback], [--no-skip-fir-cli-feedback] # 跳过 用来做统计fir-cli的反馈(用于改进fir-cli)
|
19
|
+
[--specify-app-display-name=SPECIFY_APP_DISPLAY_NAME] # 指定app 名称
|
20
|
+
-N, [--switch-to-qiniu], [--no-switch-to-qiniu] # 切换到七牛线路上传(当上传较慢时, 可试试这个)
|
21
|
+
[--oversea-turbo], [--no-oversea-turbo] # 海外加速(需联系微信 atpking 开通)
|
22
|
+
-m, [--mappingfile=MAPPINGFILE] # mappingfile
|
23
|
+
-D, [--dingtalk-access-token=DINGTALK_ACCESS_TOKEN] # 上传完毕后 若发送至钉钉, 则填写钉钉的webhook 的access_token
|
24
|
+
[--dingtalk-custom-message=DINGTALK_CUSTOM_MESSAGE] # 自定义钉钉消息 (针对钉钉新版webhook 需要校验的时候, 可以做关键字)
|
25
|
+
[--dingtalk-at-phones=DINGTALK_AT_PHONES] # 钉钉 at 某人手机号
|
26
|
+
[--dingtalk-at-all], [--no-dingtalk-at-all]
|
27
|
+
[--feishu-access-token=FEISHU_ACCESS_TOKEN] # 飞书的webhook 的access_token
|
28
|
+
[--feishu-custom-message=FEISHU_CUSTOM_MESSAGE] # 自定义飞书消息
|
29
|
+
[--wxwork-webhook=WXWORK_WEBHOOK] # 企业微信的 webhook 地址 (注意这里与 access_token 只需要填写一个参数即可)
|
30
|
+
[--wxwork-access-token=WXWORK_ACCESS_TOKEN] # 企业微信的 webhook access_token (注意这里与 access_token 只需要填写一个参数即可)
|
31
|
+
[--wxwork-custom-message=WXWORK_CUSTOM_MESSAGE] # 企业微信 的自定义消息
|
32
|
+
[--wxwork-pic-url=WXWORK_PIC_URL] # 企业微信的图片链接, best size is 1068x455
|
33
|
+
[--open], [--no-open] # 是否下载可见, 默认open
|
34
|
+
[--password=PASSWORD] # 下载页面密码, 默认为空
|
35
|
+
[--bundletool-jar-path=BUNDLETOOL_JAR_PATH] # (beta) 上传AAB 文件的特殊指令: upload aab file command: to specify bundletool.jar if command bundletool can not run directly
|
36
|
+
[--auto-download-bundletool-jar], [--no-auto-download-bundletool-jar] # (beta) 上传AAB 文件的特殊指令: upload aab file command: would download bundletool when invoke bundletool failure
|
37
|
+
-T, [--token=TOKEN] # betaqr.com(fir.im) 账户的 API_TOKEN
|
38
|
+
-L, [--logfile=LOGFILE] # Path to writable logfile
|
39
|
+
-V, [--verbose], [--no-verbose] # Show verbose
|
40
|
+
# Default: true
|
41
|
+
-q, [--quiet], [--no-quiet] # Silence commands
|
42
|
+
-h, [--help], [--no-help] # Show this help message and quit
|
43
|
+
|
44
|
+
Description:
|
45
|
+
`publish` command will publish your app file to fir.im, also the command support to publish app's short & changelog.
|
46
|
+
|
47
|
+
Example:
|
48
|
+
|
49
|
+
$ fir p <app file path> [-c <changelog> -s <custom short link> -Q -T <your api token>]
|
50
|
+
|
51
|
+
$ fir p <app file path> [-c <changelog> -s <custom short link> --password=123456 --open=false -Q -T <your api token>]
|
52
|
+
|
53
|
+
$ fir p <app file path> [-c <changelog> -s <custom short link> -m <mapping file path> -P <bughd project id> -Q -T <your
|
54
|
+
api token>]
|
24
55
|
```
|
25
56
|
|
26
|
-
相关参数详解:
|
27
57
|
|
28
|
-
- `-s` 参数, 自定义发布后的短链接地址.
|
29
|
-
- `-c` 参数, 自定义发布时的 changelog, 支持字符串与文件两种方式, 即 `--changelog='this is changelog'` 和 `--changelog='/Users/fir-cli/changelog'`.
|
30
|
-
- `-Q` 参数, 是否生成发布后二维码, 默认为不生成, 加上 `-Q` 参数后会在当前目录生成一张二维码图片, 扫描该图片即可下载该应用.
|
31
|
-
- `-R` 参数, 显示具体版本, 会在发布结束后的下载地址中生成指定的版本
|
32
|
-
- `-D` 参数, 钉钉机器人的access_token, 填写后当上传完毕, 会发送至钉钉机器人
|
33
|
-
- `--open` 参数, 设置发布后的应用是否开放给所有人下载, 关闭开放使用 `--no-open` 参数.
|
34
|
-
- `--password` 参数, 设置发布后的应用密码
|
35
58
|
|
data/fir-cli.gemspec
CHANGED
@@ -27,6 +27,11 @@ Gem::Specification.new do |spec|
|
|
27
27
|
/_/ /___/_/ |_| \____/_____/___/
|
28
28
|
|
29
29
|
## 更新记录
|
30
|
+
- (2.0.14) 第三方通知加入了 app 类型, 第三方报错将不再直接报出异常
|
31
|
+
- (2.0.13) 修复了无法跳过企业微信通知逻辑的bug
|
32
|
+
- (2.0.12) 修复因为钉钉机器人不再支持base64导致无法显示二维码,另外开始支持钉钉加签方式的鉴权, 参数为 --dingtalk_secret
|
33
|
+
- (2.0.11) 兼容了 ruby 3.0 版本, 增加了环境变量FEISHU_TIMEOUT,可以多给飞书一些超时时间
|
34
|
+
- (2.0.10) 飞书支持了 V2 版本的机器人推送
|
30
35
|
- (2.0.9) publish 支持了 企业微信通知 可以使用 --wxwork_access_token 或 --wxwork_webhook, 增加了回调超时时间至20秒
|
31
36
|
- (2.0.8) publish 支持 飞书通知, 可使用 `feishu_access_token` 和 `feishu_custom_message`, 详情见 `fir publish --help`
|
32
37
|
- (2.0.7) 修复了提示 token 有问题的错误
|
@@ -51,12 +56,13 @@ Gem::Specification.new do |spec|
|
|
51
56
|
# spec.add_development_dependency 'byebug'
|
52
57
|
spec.add_development_dependency 'minitest', '~> 5.7'
|
53
58
|
spec.add_development_dependency 'pry', '~> 0.10'
|
54
|
-
|
59
|
+
|
55
60
|
spec.add_dependency 'admqr_knife', '~> 0.1.5'
|
56
61
|
spec.add_dependency 'thor', '~> 0.19'
|
57
62
|
spec.add_dependency 'rest-client', '~> 2.0'
|
58
63
|
spec.add_dependency 'ruby_android_apk', '~> 0.7.7.1'
|
59
64
|
spec.add_dependency 'rqrcode', '~> 0.7'
|
65
|
+
spec.add_dependency 'rexml'
|
60
66
|
spec.add_dependency 'CFPropertyList'
|
61
|
-
spec.add_dependency 'api_tools', '~> 0.1.
|
67
|
+
spec.add_dependency 'api_tools', '~> 0.1.1'
|
62
68
|
end
|
data/lib/fir.rb
CHANGED
data/lib/fir/api.yml
CHANGED
@@ -2,6 +2,7 @@ fir:
|
|
2
2
|
domain: 'http://www.betaqr.com'
|
3
3
|
base_url: 'http://fir-api.admqr.com'
|
4
4
|
user_url: 'http://fir-api.admqr.com/user'
|
5
|
+
user_feishu_access_token: 'http://fir-api.admqr.com/user/fetch_feishu_v3_token'
|
5
6
|
app_url: 'http://fir-api.admqr.com/apps'
|
6
7
|
udids_url: 'http://fir-api.admqr.com/devices/multi_udid'
|
7
8
|
|
data/lib/fir/cli.rb
CHANGED
@@ -125,7 +125,8 @@ module FIR
|
|
125
125
|
method_option :dingtalk_access_token, type: :string, aliases: '-D', desc: 'Send msg to dingtalk, only need access_token, not whole url'
|
126
126
|
method_option :dingtalk_custom_message, type: :string, desc: 'add custom message to dingtalk'
|
127
127
|
method_option :dingtalk_at_phones, type: :string, desc: 'at some phones, split by , eg: 13111111111,13111111112'
|
128
|
-
method_option :dingtalk_at_all, type: :boolean,
|
128
|
+
method_option :dingtalk_at_all, type: :boolean, default: false
|
129
|
+
method_option :dingtalk_secret, type: :string, desc: 'dingtalk bot secret code (eg: SEC********)'
|
129
130
|
|
130
131
|
method_option :feishu_access_token, type: :string, desc: 'Send msg to feishu, need access_token, not whole url'
|
131
132
|
method_option :feishu_custom_message, type: :string, desc: 'add custom message to feishu'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'openssl'
|
3
|
+
require 'base64'
|
4
|
+
require 'cgi'
|
5
|
+
# require 'byebug'
|
6
|
+
class DingtalkHelper
|
7
|
+
attr_accessor :app_info, :options, :access_token, :qrcode_path, :image_key, :download_url, :title
|
8
|
+
include FIR::Config
|
9
|
+
|
10
|
+
def initialize(app_info, options, qrcode_path, download_url)
|
11
|
+
@app_info = app_info
|
12
|
+
@options = options
|
13
|
+
@access_token = @options[:dingtalk_access_token]
|
14
|
+
@secret = @options[:dingtalk_secret]
|
15
|
+
@qrcode_path = qrcode_path
|
16
|
+
@download_url = download_url
|
17
|
+
end
|
18
|
+
|
19
|
+
def send_msg
|
20
|
+
return if options[:dingtalk_access_token].blank?
|
21
|
+
|
22
|
+
api_domain = @app_info[:api_url]
|
23
|
+
title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]}) #{@app_info[:type]}"
|
24
|
+
payload = {
|
25
|
+
"msgtype": 'markdown',
|
26
|
+
"markdown": {
|
27
|
+
"title": "#{title} uploaded",
|
28
|
+
"text": "#### #{title}\n\n>uploaded at #{Time.now}\n\nurl: #{download_url}\n\n#{options[:dingtalk_custom_message]}\n\n ![](#{api_domain}/welcome/qrcode?url=#{download_url})"
|
29
|
+
}
|
30
|
+
}
|
31
|
+
build_dingtalk_at_info(payload)
|
32
|
+
url = "https://oapi.dingtalk.com/robot/send?access_token=#{options[:dingtalk_access_token]}"
|
33
|
+
if options[:dingtalk_secret]
|
34
|
+
info = secret_cal(options[:dingtalk_secret])
|
35
|
+
url = "#{url}×tamp=#{info[:timestamp]}&sign=#{info[:sign]}"
|
36
|
+
end
|
37
|
+
|
38
|
+
x = DefaultRest.post(url, payload)
|
39
|
+
rescue StandardError => e
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_dingtalk_at_info(payload)
|
43
|
+
answer = {}
|
44
|
+
answer[:isAtAll] = options[:dingtalk_at_all]
|
45
|
+
|
46
|
+
unless options[:dingtalk_at_phones].blank?
|
47
|
+
answer[:atMobiles] = options[:dingtalk_at_phones].split(',')
|
48
|
+
payload[:markdown][:text] += options[:dingtalk_at_phones].split(',').map { |x| "@#{x}" }.join(',')
|
49
|
+
end
|
50
|
+
|
51
|
+
payload[:at] = answer
|
52
|
+
end
|
53
|
+
|
54
|
+
def secret_cal(secret)
|
55
|
+
timestamp = Time.now.to_i * 1000
|
56
|
+
secret_enc = secret.encode('utf-8')
|
57
|
+
str = "#{timestamp}\n#{secret}"
|
58
|
+
string_to_sign_enc = str.encode('utf-8')
|
59
|
+
|
60
|
+
hmac = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), secret_enc, string_to_sign_enc)
|
61
|
+
{
|
62
|
+
timestamp: timestamp,
|
63
|
+
sign: CGI.escape(Base64.encode64(hmac).strip)
|
64
|
+
}
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'json'
|
2
|
+
# require 'byebug'
|
3
|
+
class FeishuHelper
|
4
|
+
attr_accessor :app_info, :options, :feishu_access_token, :qrcode_path, :image_key, :download_url, :title
|
5
|
+
include FIR::Config
|
6
|
+
|
7
|
+
def initialize(app_info, options, qrcode_path, download_url)
|
8
|
+
@app_info = app_info
|
9
|
+
@options = options
|
10
|
+
@feishu_access_token = @options[:feishu_access_token]
|
11
|
+
@qrcode_path = qrcode_path
|
12
|
+
@download_url = download_url
|
13
|
+
|
14
|
+
@title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]}) #{@app_info[:type]}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_msg
|
18
|
+
return if feishu_access_token.blank?
|
19
|
+
|
20
|
+
answer = v2_request
|
21
|
+
v1_request if answer.dig('ok') == 'false'
|
22
|
+
end
|
23
|
+
|
24
|
+
def v2_request
|
25
|
+
url = "https://open.feishu.cn/open-apis/bot/v2/hook/#{feishu_access_token}"
|
26
|
+
x = build_v2_info
|
27
|
+
# byebug
|
28
|
+
DefaultRest.post(url, x, {timeout: ENV['FEISHU_TIMEOUT'] ? ENV['FEISHU_TIMEOUT'].to_i : 30 })
|
29
|
+
end
|
30
|
+
|
31
|
+
def v1_request
|
32
|
+
url = "https://open.feishu.cn/open-apis/bot/hook/#{feishu_access_token}"
|
33
|
+
payload = {
|
34
|
+
"title": "#{title} uploaded",
|
35
|
+
"text": "#{title} uploaded at #{Time.now}\nurl: #{download_url}\n#{options[:feishu_custom_message]}\n"
|
36
|
+
}
|
37
|
+
DefaultRest.post(url, payload, {timeout: ENV['FEISHU_TIMEOUT'] ? ENV['FEISHU_TIMEOUT'].to_i : 30 })
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def build_v2_info
|
43
|
+
{
|
44
|
+
msg_type: 'post',
|
45
|
+
content: {
|
46
|
+
post: {
|
47
|
+
zh_cn: {
|
48
|
+
title: title,
|
49
|
+
content: [build_info_tags, [build_image_tag]]
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_info_tags
|
57
|
+
text = "#{title} uploaded at #{Time.now}\n#{options[:feishu_custom_message]}\n"
|
58
|
+
[
|
59
|
+
{
|
60
|
+
"tag": 'text',
|
61
|
+
"text": text
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"tag": 'a',
|
65
|
+
"text": download_url,
|
66
|
+
"href": download_url
|
67
|
+
}
|
68
|
+
]
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_image_tag
|
72
|
+
{
|
73
|
+
"tag": 'img',
|
74
|
+
"image_key": upload_qr_code,
|
75
|
+
"width": 300,
|
76
|
+
"height": 300
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def fetch_image_access_token
|
81
|
+
answer = DefaultRest.post(fir_api[:fetch_feishu_v3_token] || 'http://fir-api.admqr.com/user/fetch_feishu_v3_token', {})
|
82
|
+
answer[:feishu_v3_token]
|
83
|
+
end
|
84
|
+
|
85
|
+
def upload_qr_code
|
86
|
+
answer = RestClient.post('https://open.feishu.cn/open-apis/image/v4/put/', {
|
87
|
+
image_type: 'message',
|
88
|
+
image: File.new(qrcode_path, 'rb')
|
89
|
+
}, {
|
90
|
+
'Authorization' => "Bearer #{fetch_image_access_token}"
|
91
|
+
})
|
92
|
+
|
93
|
+
json = JSON.parse(answer)
|
94
|
+
json.dig('data', 'image_key')
|
95
|
+
end
|
96
|
+
end
|
data/lib/fir/util/publish.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# require 'byebug'
|
4
|
+
|
5
|
+
require_relative './third_notifier_module'
|
4
6
|
require_relative './qiniu_uploader'
|
5
7
|
require_relative './ali_uploader'
|
8
|
+
require_relative '../util/feishu_helper'
|
9
|
+
require_relative '../util/dingtalk_helper'
|
6
10
|
|
7
11
|
module FIR
|
8
12
|
module Publish
|
13
|
+
include FIR::ThirdNotifierModule
|
9
14
|
def publish(*args, options)
|
10
15
|
initialize_publish_options(args, options)
|
11
16
|
logger_info_publishing_message
|
12
17
|
|
13
18
|
logger.info 'begin to upload ...'
|
19
|
+
logger.info "fir-cli version #{FIR::VERSION} (#{RUBY_VERSION} @ #{RUBY_PLATFORM})"
|
14
20
|
received_app_info = upload_app
|
15
21
|
|
16
22
|
short = received_app_info[:short]
|
@@ -25,10 +31,8 @@ module FIR
|
|
25
31
|
logger.info "Published succeed: #{download_url}"
|
26
32
|
|
27
33
|
qrcode_path = build_qrcode download_url
|
28
|
-
|
29
|
-
|
30
|
-
feishu_notifier(download_url)
|
31
|
-
wxwork_notifier(download_url)
|
34
|
+
|
35
|
+
notify_to_thirds(download_url, qrcode_path)
|
32
36
|
|
33
37
|
upload_mapping_file_with_publish
|
34
38
|
|
@@ -92,9 +96,7 @@ module FIR
|
|
92
96
|
app_info_dict
|
93
97
|
rescue StandardError => e
|
94
98
|
puts e.message
|
95
|
-
if e.respond_to?(e.response) && e.respond_to?(e.response.body)
|
96
|
-
puts e.response.body
|
97
|
-
end
|
99
|
+
puts e.response.body if e.respond_to?(e.response) && e.respond_to?(e.response.body)
|
98
100
|
raise e
|
99
101
|
end
|
100
102
|
|
@@ -193,59 +195,6 @@ module FIR
|
|
193
195
|
api_token: @token
|
194
196
|
end
|
195
197
|
|
196
|
-
def dingtalk_notifier(download_url, qrcode_path)
|
197
|
-
return if options[:dingtalk_access_token].blank?
|
198
|
-
|
199
|
-
title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
|
200
|
-
payload = {
|
201
|
-
"msgtype": 'markdown',
|
202
|
-
"markdown": {
|
203
|
-
"title": "#{title} uploaded",
|
204
|
-
"text": "#{title} uploaded at #{Time.now}\nurl: #{download_url}\n#{options[:dingtalk_custom_message]}\n![app二维码](data:image/png;base64,#{Base64.strict_encode64(File.read(open(qrcode_path)))})"
|
205
|
-
# "text": "#{title} uploaded at #{Time.now}\nurl: #{download_url}\n#{options[:dingtalk_custom_message]}\n"
|
206
|
-
}
|
207
|
-
}
|
208
|
-
build_dingtalk_at_info(payload)
|
209
|
-
url = "https://oapi.dingtalk.com/robot/send?access_token=#{options[:dingtalk_access_token]}"
|
210
|
-
|
211
|
-
# 用完了二维码, 就删了
|
212
|
-
File.delete(qrcode_path) unless @export_qrcode
|
213
|
-
|
214
|
-
DefaultRest.post(url, payload)
|
215
|
-
rescue StandardError => e
|
216
|
-
logger.warn "Dingtalk send error #{e.message}"
|
217
|
-
end
|
218
|
-
|
219
|
-
def feishu_notifier(download_url)
|
220
|
-
return if options[:feishu_access_token].blank?
|
221
|
-
title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
|
222
|
-
url = "https://open.feishu.cn/open-apis/bot/hook/#{options[:feishu_access_token]}"
|
223
|
-
payload = {
|
224
|
-
"title": "#{title} uploaded",
|
225
|
-
"text": "#{title} uploaded at #{Time.now}\nurl: #{download_url}\n#{options[:feishu_custom_message]}\n"
|
226
|
-
}
|
227
|
-
DefaultRest.post(url, payload)
|
228
|
-
end
|
229
|
-
|
230
|
-
def wxwork_notifier(download_url)
|
231
|
-
webhook_url = options[:wxwork_webhook]
|
232
|
-
webhook_url ||= "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=#{options[:wxwork_access_token]}"
|
233
|
-
return if webhook_url.blank?
|
234
|
-
|
235
|
-
title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
|
236
|
-
payload = {
|
237
|
-
"msgtype": "news",
|
238
|
-
"news": {
|
239
|
-
"articles": [{
|
240
|
-
"title": "#{title} uploaded",
|
241
|
-
"description": "#{title} uploaded at #{Time.now}\nurl: #{download_url}\n#{options[:wxwork_custom_message]}\n",
|
242
|
-
"url": download_url,
|
243
|
-
"picurl": options[:wxwork_pic_url]
|
244
|
-
}],
|
245
|
-
},
|
246
|
-
}
|
247
|
-
DefaultRest.post(webhook_url, payload)
|
248
|
-
end
|
249
198
|
|
250
199
|
def initialize_publish_options(args, options)
|
251
200
|
@options = options
|
@@ -254,10 +203,10 @@ module FIR
|
|
254
203
|
|
255
204
|
check_file_exist(@file_path)
|
256
205
|
check_supported_file(@file_path)
|
257
|
-
|
206
|
+
|
258
207
|
@token = options[:token] || current_token
|
259
208
|
check_token_cannot_be_blank(@token)
|
260
|
-
|
209
|
+
|
261
210
|
@changelog = read_changelog(options[:changelog]).to_s.to_utf8
|
262
211
|
@short = options[:short].to_s
|
263
212
|
@passwd = options[:password].to_s
|
@@ -269,7 +218,10 @@ module FIR
|
|
269
218
|
@app_id = @uploading_info[:id]
|
270
219
|
|
271
220
|
@skip_update_icon = options[:skip_update_icon]
|
221
|
+
|
272
222
|
@force_pin_history = options[:force_pin_history]
|
223
|
+
|
224
|
+
@app_info[:api_url] = fir_api[:base_url]
|
273
225
|
unless options[:specify_icon_file].blank?
|
274
226
|
@specify_icon_file_path = File.absolute_path(options[:specify_icon_file])
|
275
227
|
end
|
@@ -280,19 +232,5 @@ module FIR
|
|
280
232
|
|
281
233
|
File.exist?(changelog) ? File.read(changelog) : changelog
|
282
234
|
end
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
def build_dingtalk_at_info(payload)
|
287
|
-
answer = {}
|
288
|
-
answer[:isAtAll] = options[:dingtalk_at_all]
|
289
|
-
|
290
|
-
unless options[:dingtalk_at_phones].blank?
|
291
|
-
answer[:atMobiles] = options[:dingtalk_at_phones].split(',')
|
292
|
-
payload[:markdown][:text] += options[:dingtalk_at_phones].split(',').map { |x| "@#{x}" }.join(',')
|
293
|
-
end
|
294
|
-
|
295
|
-
payload[:at] = answer
|
296
|
-
end
|
297
235
|
end
|
298
236
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module FIR
|
2
|
+
module ThirdNotifierModule
|
3
|
+
def notify_to_thirds(download_url, qrcode_path)
|
4
|
+
dingtalk_notifier(download_url, qrcode_path)
|
5
|
+
feishu_notifier(download_url, qrcode_path)
|
6
|
+
wxwork_notifier(download_url)
|
7
|
+
rescue => e
|
8
|
+
logger.warn "third notifiers error #{e.message}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def dingtalk_notifier(download_url, qrcode_path)
|
12
|
+
DingtalkHelper.new(@app_info, options, qrcode_path, download_url).send_msg
|
13
|
+
end
|
14
|
+
|
15
|
+
def feishu_notifier(download_url, qrcode_path)
|
16
|
+
FeishuHelper.new(@app_info, options, qrcode_path, download_url).send_msg
|
17
|
+
end
|
18
|
+
|
19
|
+
def wxwork_notifier(download_url)
|
20
|
+
return if options[:wxwork_webhook].blank? && options[:wxwork_access_token].blank?
|
21
|
+
|
22
|
+
webhook_url = options[:wxwork_webhook]
|
23
|
+
webhook_url ||= "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=#{options[:wxwork_access_token]}"
|
24
|
+
|
25
|
+
title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]}) #{@app_info[:type]}"
|
26
|
+
payload = {
|
27
|
+
"msgtype": 'news',
|
28
|
+
"news": {
|
29
|
+
"articles": [{
|
30
|
+
"title": "#{title} uploaded",
|
31
|
+
"description": "#{title} uploaded at #{Time.now}\nurl: #{download_url}\n#{options[:wxwork_custom_message]}\n",
|
32
|
+
"url": download_url,
|
33
|
+
"picurl": options[:wxwork_pic_url]
|
34
|
+
}]
|
35
|
+
}
|
36
|
+
}
|
37
|
+
DefaultRest.post(webhook_url, payload)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/fir/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
# CodeClimate::TestReporter.start
|
4
|
-
require 'codeclimate-test-reporter'
|
5
|
-
require 'simplecov'
|
6
|
-
SimpleCov.start
|
7
4
|
|
8
5
|
|
9
6
|
require 'minitest/autorun'
|
@@ -80,7 +77,7 @@ end
|
|
80
77
|
|
81
78
|
# 跑完测试之后再发结果到Codelimate
|
82
79
|
# 测试CODECLIMATE_REPO_TOKEN: c454b9a54151b3ed3e18949279aec49d6a25bf507706815f99a919f1c01679ed
|
83
|
-
Minitest.after_run do
|
80
|
+
Minitest.after_run do
|
84
81
|
COVERAGE_FILE = "coverage/.resultset.json".freeze
|
85
82
|
if (repo_token = ENV["CODECLIMATE_REPO_TOKEN"]) && !repo_token.empty?
|
86
83
|
if File.exist?(COVERAGE_FILE)
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fir-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- NaixSpirit
|
8
8
|
- atpking
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-06-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -137,6 +137,20 @@ dependencies:
|
|
137
137
|
- - "~>"
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '0.7'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: rexml
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :runtime
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
140
154
|
- !ruby/object:Gem::Dependency
|
141
155
|
name: CFPropertyList
|
142
156
|
requirement: !ruby/object:Gem::Requirement
|
@@ -157,14 +171,14 @@ dependencies:
|
|
157
171
|
requirements:
|
158
172
|
- - "~>"
|
159
173
|
- !ruby/object:Gem::Version
|
160
|
-
version: 0.1.
|
174
|
+
version: 0.1.1
|
161
175
|
type: :runtime
|
162
176
|
prerelease: false
|
163
177
|
version_requirements: !ruby/object:Gem::Requirement
|
164
178
|
requirements:
|
165
179
|
- - "~>"
|
166
180
|
- !ruby/object:Gem::Version
|
167
|
-
version: 0.1.
|
181
|
+
version: 0.1.1
|
168
182
|
description: fir.im command tool, support iOS and Android
|
169
183
|
email:
|
170
184
|
- atpking@gmail.com
|
@@ -218,6 +232,8 @@ files:
|
|
218
232
|
- lib/fir/util/build_common.rb
|
219
233
|
- lib/fir/util/build_ipa.rb
|
220
234
|
- lib/fir/util/config.rb
|
235
|
+
- lib/fir/util/dingtalk_helper.rb
|
236
|
+
- lib/fir/util/feishu_helper.rb
|
221
237
|
- lib/fir/util/http.rb
|
222
238
|
- lib/fir/util/info.rb
|
223
239
|
- lib/fir/util/login.rb
|
@@ -232,6 +248,7 @@ files:
|
|
232
248
|
- lib/fir/util/publish.rb
|
233
249
|
- lib/fir/util/publisher.rb
|
234
250
|
- lib/fir/util/qiniu_uploader.rb
|
251
|
+
- lib/fir/util/third_notifier_module.rb
|
235
252
|
- lib/fir/version.rb
|
236
253
|
- lib/fir/xcode_wrapper.sh
|
237
254
|
- lib/fir_cli.rb
|
@@ -254,7 +271,10 @@ metadata: {}
|
|
254
271
|
post_install_message: "\n ______________ ________ ____\n /
|
255
272
|
____/ _/ __ \\ / ____/ / / _/\n / /_ / // /_/ /_____/ / / / /
|
256
273
|
/\n / __/ _/ // _, _/_____/ /___/ /____/ /\n /_/ /___/_/ |_| \\____/_____/___/\n\n
|
257
|
-
\ ## 更新记录\n - (2.0.
|
274
|
+
\ ## 更新记录\n - (2.0.14) 第三方通知加入了 app 类型, 第三方报错将不再直接报出异常\n - (2.0.13) 修复了无法跳过企业微信通知逻辑的bug\n
|
275
|
+
\ - (2.0.12) 修复因为钉钉机器人不再支持base64导致无法显示二维码,另外开始支持钉钉加签方式的鉴权, 参数为 --dingtalk_secret\n
|
276
|
+
\ - (2.0.11) 兼容了 ruby 3.0 版本, 增加了环境变量FEISHU_TIMEOUT,可以多给飞书一些超时时间\n - (2.0.10) 飞书支持了
|
277
|
+
V2 版本的机器人推送\n - (2.0.9) publish 支持了 企业微信通知 可以使用 --wxwork_access_token 或 --wxwork_webhook,
|
258
278
|
增加了回调超时时间至20秒\n - (2.0.8) publish 支持 飞书通知, 可使用 `feishu_access_token` 和 `feishu_custom_message`,
|
259
279
|
详情见 `fir publish --help`\n - (2.0.7) 修复了提示 token 有问题的错误\n - (2.0.6) 将校验文件是否存在提前\n
|
260
280
|
\ - (2.0.5) 更换了上传域名, 避免与 深信服的设备冲突\n - (2.0.4) 修复了 cdn 不支持 patch 方法透传, 导致在修改 app
|
@@ -284,7 +304,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
284
304
|
version: '0'
|
285
305
|
requirements: []
|
286
306
|
rubygems_version: 3.0.3
|
287
|
-
signing_key:
|
307
|
+
signing_key:
|
288
308
|
specification_version: 4
|
289
309
|
summary: fir.im command tool
|
290
310
|
test_files:
|