fir-cli-x 1.7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +8 -0
  3. data/.dockerignore +2 -0
  4. data/.flow-plugin.yml +14 -0
  5. data/.gitignore +27 -0
  6. data/.travis.yml +23 -0
  7. data/CHANGELOG +194 -0
  8. data/Dockerfile +12 -0
  9. data/Gemfile +10 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +63 -0
  12. data/Rakefile +10 -0
  13. data/bin/console +11 -0
  14. data/bin/fir +14 -0
  15. data/bin/setup +7 -0
  16. data/doc/help.md +34 -0
  17. data/doc/info.md +44 -0
  18. data/doc/install.md +67 -0
  19. data/doc/login.md +19 -0
  20. data/doc/publish.md +35 -0
  21. data/doc/upgrade.md +7 -0
  22. data/fir-cli.gemspec +52 -0
  23. data/fir.sh +46 -0
  24. data/install.sh +210 -0
  25. data/lib/fir-cli.rb +3 -0
  26. data/lib/fir.rb +28 -0
  27. data/lib/fir/api.yml +7 -0
  28. data/lib/fir/cli.rb +181 -0
  29. data/lib/fir/patches.rb +10 -0
  30. data/lib/fir/patches/blank.rb +131 -0
  31. data/lib/fir/patches/concern.rb +146 -0
  32. data/lib/fir/patches/default_headers.rb +9 -0
  33. data/lib/fir/patches/hash.rb +79 -0
  34. data/lib/fir/patches/instance_variables.rb +30 -0
  35. data/lib/fir/patches/native_patch.rb +28 -0
  36. data/lib/fir/patches/os_patch.rb +28 -0
  37. data/lib/fir/patches/try.rb +102 -0
  38. data/lib/fir/util.rb +86 -0
  39. data/lib/fir/util/build_apk.rb +77 -0
  40. data/lib/fir/util/build_common.rb +93 -0
  41. data/lib/fir/util/build_ipa.rb +11 -0
  42. data/lib/fir/util/config.rb +43 -0
  43. data/lib/fir/util/http.rb +23 -0
  44. data/lib/fir/util/info.rb +38 -0
  45. data/lib/fir/util/login.rb +17 -0
  46. data/lib/fir/util/mapping.rb +98 -0
  47. data/lib/fir/util/me.rb +19 -0
  48. data/lib/fir/util/parser/apk.rb +46 -0
  49. data/lib/fir/util/parser/bin/pngcrush +0 -0
  50. data/lib/fir/util/parser/common.rb +24 -0
  51. data/lib/fir/util/parser/ipa.rb +188 -0
  52. data/lib/fir/util/parser/pngcrush.rb +23 -0
  53. data/lib/fir/util/publish.rb +253 -0
  54. data/lib/fir/version.rb +5 -0
  55. data/lib/fir/xcode_wrapper.sh +29 -0
  56. data/lib/fir_cli.rb +3 -0
  57. data/test/build_ipa_test.rb +17 -0
  58. data/test/cases/test_apk.apk +0 -0
  59. data/test/cases/test_apk_txt +1 -0
  60. data/test/cases/test_ipa.ipa +0 -0
  61. data/test/cases/test_ipa_dsym +0 -0
  62. data/test/info_test.rb +36 -0
  63. data/test/login_test.rb +12 -0
  64. data/test/mapping_test.rb +18 -0
  65. data/test/me_test.rb +17 -0
  66. data/test/publish_test.rb +44 -0
  67. data/test/test_helper.rb +98 -0
  68. metadata +273 -0
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ module FIR
4
+ module Parser
5
+ module Pngcrush
6
+
7
+ class << self
8
+
9
+ def png_bin
10
+ @png_bin ||= File.expand_path('../bin/pngcrush', __FILE__)
11
+ end
12
+
13
+ def uncrush_icon crushed_icon_path, uncrushed_icon_path
14
+ system("#{png_bin} -revert-iphone-optimizations #{crushed_icon_path} #{uncrushed_icon_path} &> /dev/null")
15
+ end
16
+
17
+ def crush_icon uncrushed_icon_path, crushed_icon_path
18
+ system("#{png_bin} -iphone #{uncrushed_icon_path} #{crushed_icon_path} &> /dev/null")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+ module FIR
3
+ module Publish
4
+ def publish(*args, options)
5
+ initialize_publish_options(args, options)
6
+ check_supported_file_and_token
7
+
8
+ logger_info_publishing_message
9
+
10
+ @app_info = send("#{@file_type}_info", @file_path, full_info: true)
11
+ @user_info = fetch_user_info(@token)
12
+ @uploading_info = fetch_uploading_info
13
+ @app_id = @uploading_info[:id]
14
+
15
+ upload_app
16
+
17
+ logger_info_dividing_line
18
+ logger_info_app_short_and_qrcode(options)
19
+
20
+ dingtalk_notifier(options)
21
+ upload_mapping_file_with_publish(options)
22
+ logger_info_blank_line
23
+ clean_files
24
+ end
25
+
26
+ def logger_info_publishing_message
27
+ user_info = fetch_user_info(@token)
28
+
29
+ email = user_info.fetch(:email, '')
30
+ name = user_info.fetch(:name, '')
31
+
32
+ logger.info "Publishing app via #{name}<#{email}>......."
33
+ logger_info_dividing_line
34
+ end
35
+
36
+ def upload_app
37
+ @icon_cert = @uploading_info[:cert][:icon]
38
+ @binary_cert = @uploading_info[:cert][:binary]
39
+
40
+ upload_app_icon unless @app_info[:icons].blank?
41
+ @app_uploaded_callback_data = upload_app_binary
42
+ logger.info "App id is #{@app_id}"
43
+ logger.info "Release id is #{@app_uploaded_callback_data[:release_id]}"
44
+ upload_device_info
45
+ update_app_info
46
+ fetch_app_info
47
+ end
48
+
49
+ %w[binary icon].each do |word|
50
+ define_method("upload_app_#{word}") do
51
+ upload_file(word)
52
+ storage = ENV['SOTRAGE_NAME'] || 'qiniu'
53
+ post("#{fir_api[:base_url]}/auth/#{storage}/callback", send("#{word}_information"))
54
+ end
55
+ end
56
+
57
+ def upload_file(postfix)
58
+ logger.info "Uploading app #{postfix}......"
59
+ url = @uploading_info[:cert][postfix.to_sym][:upload_url]
60
+ info = send("uploading_#{postfix}_info")
61
+ logger.debug "url = #{url}, info = #{info}"
62
+ uploaded_info = post(url, info.merge(manual_callback: true),
63
+ params_to_json: false,
64
+ header: nil)
65
+ rescue StandardError
66
+ logger.error "Uploading app #{postfix} failed"
67
+ exit 1
68
+ end
69
+
70
+ def uploading_icon_info
71
+ large_icon_path = @app_info[:icons].max_by { |f| File.size(f) }
72
+ @uncrushed_icon_path = convert_icon(large_icon_path)
73
+ {
74
+ key: @icon_cert[:key],
75
+ token: @icon_cert[:token],
76
+ file: File.new(@uncrushed_icon_path, 'rb'),
77
+ 'x:is_converted' => '1'
78
+ }
79
+ end
80
+
81
+ def icon_information
82
+ {
83
+ key: @icon_cert[:key],
84
+ token: @icon_cert[:token],
85
+ origin: 'fir-cli',
86
+ parent_id: @app_id,
87
+ fsize: File.size(@uncrushed_icon_path),
88
+ fname: 'blob'
89
+ }
90
+ end
91
+
92
+ def binary_information
93
+ {
94
+ build: @app_info[:build],
95
+ fname: File.basename(@file_path),
96
+ key: @binary_cert[:key],
97
+ name: @app_info[:display_name] || @app_info[:name],
98
+ origin: 'fir-cli',
99
+ parent_id: @app_id,
100
+ release_tag: 'develop',
101
+ fsize: File.size(@file_path),
102
+ release_type: @app_info[:release_type],
103
+ distribution_name: @app_info[:distribution_name],
104
+ token: @binary_cert[:token],
105
+ version: @app_info[:version],
106
+ changelog: @changelog,
107
+ user_id: @user_info[:id]
108
+ }.reject { |x| x.nil? || x == '' }
109
+ end
110
+
111
+ def uploading_binary_info
112
+ {
113
+ key: @binary_cert[:key],
114
+ token: @binary_cert[:token],
115
+ file: File.new(@file_path, 'rb'),
116
+ # Custom variables
117
+ 'x:name' => @app_info[:display_name] || @app_info[:name],
118
+ 'x:build' => @app_info[:build],
119
+ 'x:version' => @app_info[:version],
120
+ 'x:changelog' => @changelog,
121
+ 'x:release_type' => @app_info[:release_type],
122
+ 'x:distribution_name' => @app_info[:distribution_name]
123
+ }
124
+ end
125
+
126
+ def upload_device_info
127
+ return if @app_info[:devices].blank?
128
+
129
+ logger.info 'Updating devices info......'
130
+
131
+ post fir_api[:udids_url], key: @binary_cert[:key],
132
+ udids: @app_info[:devices].join(','),
133
+ api_token: @token
134
+ end
135
+
136
+ def update_app_info
137
+ update_info = { short: @short, passwd: @passwd, is_opened: @is_opened }.compact
138
+
139
+ return if update_info.blank?
140
+
141
+ logger.info 'Updating app info......'
142
+
143
+ patch fir_api[:app_url] + "/#{@app_id}", update_info.merge(api_token: @token)
144
+ end
145
+
146
+ def fetch_uploading_info
147
+ logger.info "Fetching #{@app_info[:identifier]}@fir.im uploading info......"
148
+ logger.info "Uploading app: #{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
149
+
150
+ post fir_api[:app_url], type: @app_info[:type],
151
+ bundle_id: @app_info[:identifier],
152
+ manual_callback: true,
153
+ api_token: @token
154
+ end
155
+
156
+ def fetch_release_id
157
+ get "#{fir_api[:base_url]}/apps/#{@app_id}/releases/find_release_by_key", api_token: @token, key: @binary_cert[:key]
158
+ end
159
+
160
+ def fetch_app_info
161
+ logger.info 'Fetch app info from fir.im'
162
+
163
+ @fir_app_info = get(fir_api[:app_url] + "/#{@app_id}", api_token: @token)
164
+ write_app_info(id: @fir_app_info[:id], short: @fir_app_info[:short], name: @fir_app_info[:name])
165
+ @fir_app_info
166
+ end
167
+
168
+ def upload_mapping_file_with_publish(options)
169
+ return if !options[:mappingfile] || !options[:proj]
170
+
171
+ logger_info_blank_line
172
+
173
+ mapping options[:mappingfile], proj: options[:proj],
174
+ build: @app_info[:build],
175
+ version: @app_info[:version],
176
+ token: @token
177
+ end
178
+
179
+ def logger_info_app_short_and_qrcode(options)
180
+ @download_url = "#{fir_api[:domain]}/#{@fir_app_info[:short]}"
181
+ @download_url += "?release_id=#{@app_uploaded_callback_data[:release_id]}" if !!options[:need_release_id]
182
+
183
+ logger.info "Published succeed: #{@download_url}"
184
+
185
+ @qrcode_path = "#{File.dirname(@file_path)}/fir-#{@app_info[:name]}.png"
186
+ FIR.generate_rqrcode(@download_url, @qrcode_path)
187
+
188
+ logger.info "Local qrcode file: #{@qrcode_path}" if @export_qrcode
189
+ end
190
+
191
+ private
192
+
193
+ def clean_files
194
+ File.delete(@qrcode_path) unless @export_qrcode
195
+ end
196
+
197
+ def dingtalk_notifier(options)
198
+ if options[:dingtalk_access_token]
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 ![app二维码](data:image/png;base64,#{Base64.strict_encode64(File.read(open(@qrcode_path)))})"
205
+ }
206
+ }
207
+ url = "https://oapi.dingtalk.com/robot/send?access_token=#{options[:dingtalk_access_token]}"
208
+ DefaultRest.post(url, payload)
209
+ end
210
+ rescue StandardError => e
211
+ logger.warn "Dingtalk send error #{e.message}"
212
+ end
213
+
214
+ def initialize_publish_options(args, options)
215
+ @file_path = File.absolute_path(args.first.to_s)
216
+ @file_type = File.extname(@file_path).delete('.')
217
+ @token = options[:token] || current_token
218
+ @changelog = read_changelog(options[:changelog]).to_s.to_utf8
219
+ @short = options[:short].to_s
220
+ @passwd = options[:password].to_s
221
+ @is_opened = @passwd.blank? ? options[:open] : false
222
+ @export_qrcode = !!options[:qrcode]
223
+ end
224
+
225
+ def read_changelog(changelog)
226
+ return if changelog.blank?
227
+
228
+ File.exist?(changelog) ? File.read(changelog) : changelog
229
+ end
230
+
231
+ def check_supported_file_and_token
232
+ check_file_exist(@file_path)
233
+ check_supported_file(@file_path)
234
+ check_token_cannot_be_blank(@token)
235
+ fetch_user_info(@token)
236
+ end
237
+
238
+ def convert_icon(origin_path)
239
+ # 兼容性不太好, 蔽掉转化图标
240
+ return origin_path
241
+
242
+ logger.info "Converting app's icon......"
243
+
244
+ if @app_info[:type] == 'ios'
245
+ output_path = Tempfile.new(['uncrushed_icon', '.png']).path
246
+ FIR::Parser::Pngcrush.uncrush_icon(origin_path, output_path)
247
+ origin_path = output_path if File.size(output_path) != 0
248
+ end
249
+
250
+ origin_path
251
+ end
252
+ end
253
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module FIR
4
+ VERSION = '1.7.2.1'
5
+ end
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+
3
+ #!/bin/bash --login
4
+
5
+ which rvm > /dev/null
6
+
7
+ if [[ $? -eq 0 ]]; then
8
+ echo "RVM detected, forcing to use system ruby since xcodebuild cause error"
9
+ [[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
10
+ rvm use system
11
+ fi
12
+
13
+ if which rbenv > /dev/null; then
14
+ echo "rbenv detected, removing env variables since xcodebuild cause error"
15
+ rbenv shell system
16
+ fi
17
+
18
+ shell_session_update() { :; }
19
+
20
+ unset RUBYLIB
21
+ unset RUBYOPT
22
+ unset BUNDLE_BIN_PATH
23
+ unset _ORIGINAL_GEM_PATH
24
+ unset BUNDLE_GEMFILE
25
+ unset GEM_HOME
26
+ unset GEM_PATH
27
+
28
+ set -x
29
+ xcodebuild "$@"
data/lib/fir_cli.rb ADDED
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+ # This file exists for backward compatbility with require 'fir_cli'
3
+ require_relative './fir'
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ class BuildAppTest < Minitest::Test
4
+
5
+ def test_build_app
6
+ if ENV['BUILD_TEST']
7
+ options = OpenStruct.new
8
+ options.send('publish?=', true)
9
+
10
+ assert FIR.build_ipa(default_ipa_project, options)
11
+ assert FIR.build_apk(default_apk_project, options)
12
+
13
+ assert FIR.build_ipa(default_ipa_git_url, options)
14
+ assert FIR.build_apk(default_apk_git_url, options)
15
+ end
16
+ end
17
+ end
Binary file
@@ -0,0 +1 @@
1
+ this is a text
Binary file
Binary file
data/test/info_test.rb ADDED
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ class InfoTest < Minitest::Test
4
+
5
+ def test_apk_info
6
+ info = FIR.apk_info(default_apk, full_info: true)
7
+
8
+ assert_equal 'android', info[:type]
9
+ assert_equal 'com.bughd.myapplication', info[:identifier]
10
+ assert_equal 'My Application', info[:name]
11
+ assert_equal '1', info[:build]
12
+ assert_equal '1.0', info[:version]
13
+
14
+ assert_equal true, File.exist?(info[:icons].first)
15
+
16
+ assert FIR.info(default_apk, {})
17
+ end
18
+
19
+ def test_ipa_info
20
+ info = FIR.ipa_info(default_ipa, full_info: true)
21
+
22
+ assert_equal 'ios', info[:type]
23
+ assert_equal 'im.fir.build-ipa', info[:identifier]
24
+ assert_equal 'build_ipa', info[:name]
25
+ assert_equal '1', info[:build]
26
+ assert_equal '1.0', info[:version]
27
+
28
+ # Only for OSX
29
+ # assert_equal nil, info[:display_name]
30
+ # assert_equal default_device_udid, info[:devices].first
31
+ # assert_equal 'adhoc', info[:release_type]
32
+ # assert_equal default_distribution_name, info[:distribution_name]
33
+
34
+ assert FIR.info(default_ipa, {})
35
+ end
36
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ class LoginTest < Minitest::Test
4
+
5
+ def test_login
6
+ user_info = FIR.fetch_user_info(default_token)
7
+
8
+ assert_equal default_email, user_info.fetch(:email, '')
9
+
10
+ assert FIR.login(default_token)
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ class MappingTest < Minitest::Test
4
+
5
+ def test_mapping
6
+ options = {
7
+ token: default_token,
8
+ version: '1.1',
9
+ build: '1'
10
+ }
11
+
12
+ if ENV['MAPPING_TEST']
13
+ assert FIR.mapping(default_dsym_mapping, options.merge(proj: default_bughd_project_ios_id))
14
+ assert FIR.mapping(default_txt_mapping, options.merge(proj: default_bughd_project_android_id))
15
+ assert FIR.mapping(bigger_txt_mapping, options.merge(proj: default_bughd_project_android_id))
16
+ end
17
+ end
18
+ end
data/test/me_test.rb ADDED
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ class MeTest < Minitest::Test
4
+
5
+ def test_me
6
+ user_info = FIR.fetch_user_info(default_token)
7
+
8
+ FIR.write_config(email: user_info.fetch(:email, ''), token: default_token)
9
+ FIR.reload_config
10
+
11
+ me_info = FIR.fetch_user_info(FIR.current_token)
12
+
13
+ assert_equal default_email, me_info.fetch(:email, '')
14
+
15
+ assert FIR.me
16
+ end
17
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ class PublishTest < Minitest::Test
4
+
5
+ def setup
6
+ @options = {
7
+ token: default_token,
8
+ changelog: "test from fir-cli #{Time.now.to_i}"
9
+ }
10
+ end
11
+
12
+ def test_simple_publish
13
+ assert FIR.publish(default_ipa, @options)
14
+ assert FIR.publish(default_apk, @options)
15
+ end
16
+
17
+ def test_update_app_info
18
+ short = SecureRandom.hex[3..9]
19
+ is_opened = (rand(100) % 2) == 0
20
+
21
+ update_info = { short: short, open: is_opened }
22
+ FIR.publish(default_ipa, @options.merge(update_info))
23
+
24
+ info = FIR.fetch_app_info
25
+
26
+ assert_equal short, info[:short]
27
+ assert_equal is_opened, info[:is_opened]
28
+ end
29
+
30
+ def test_update_app_passwd
31
+ short = SecureRandom.hex[3..9]
32
+ is_opened = (rand(100) % 2) == 0
33
+ passwd = SecureRandom.hex[0..9]
34
+
35
+ update_info = { short: short, password: passwd, open: is_opened }
36
+ FIR.publish(default_ipa, @options.merge(update_info))
37
+
38
+ info = FIR.fetch_app_info
39
+
40
+ assert_equal short, info[:short]
41
+ assert_equal passwd, info[:passwd]
42
+ assert_equal false, info[:is_opened]
43
+ end
44
+ end