fir-cli-x 1.7.2.1

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.
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