fir-cli-xsl 1.0.3 → 1.0.4

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 +4 -4
  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 +67 -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 +192 -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 +293 -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 +84 -4
@@ -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,293 @@
1
+ # frozen_string_literal: true
2
+ module FIR
3
+ module Publish
4
+ def dingtalk(*args, options)
5
+ initialize_dtalk_options
6
+ if @token
7
+ payload = {
8
+ "msgtype": 'markdown',
9
+ "markdown": {
10
+ "title": "ceshi",
11
+ "text": @content
12
+ }
13
+ }
14
+ url = "https://oapi.dingtalk.com/robot/send?access_token=#{@token}"
15
+ DefaultRest.post(url, payload)
16
+ end
17
+
18
+ def initialize_dtalk_options(args, options)
19
+ @token = options[:token] || current_token
20
+ @content = options[:content].to_s
21
+ end
22
+
23
+ def publish(*args, options)
24
+ initialize_publish_options(args, options)
25
+ check_supported_file_and_token
26
+
27
+ logger_info_publishing_message
28
+
29
+ @app_info = send("#{@file_type}_info", @file_path, full_info: true)
30
+ @user_info = fetch_user_info(@token)
31
+ @uploading_info = fetch_uploading_info
32
+ @app_id = @uploading_info[:id]
33
+
34
+ upload_app
35
+
36
+ logger_info_dividing_line
37
+ logger_info_app_short_and_qrcode(options)
38
+
39
+ dingtalk_notifier(options)
40
+ upload_mapping_file_with_publish(options)
41
+ logger_info_blank_line
42
+ clean_files
43
+ end
44
+
45
+ def logger_info_publishing_message
46
+ user_info = fetch_user_info(@token)
47
+
48
+ email = user_info.fetch(:email, '')
49
+ name = user_info.fetch(:name, '')
50
+
51
+ logger.info "Publishing app via #{name}<#{email}>......."
52
+ logger_info_dividing_line
53
+ end
54
+
55
+ def upload_app
56
+ @icon_cert = @uploading_info[:cert][:icon]
57
+ @binary_cert = @uploading_info[:cert][:binary]
58
+
59
+ fetch_release_info
60
+ upload_app_icon unless @app_info[:icons].blank?
61
+ @app_uploaded_callback_data = upload_app_binary
62
+ logger.info "App id is #{@app_id}"
63
+ logger.info "Release id is #{@app_uploaded_callback_data[:release_id]}"
64
+ upload_device_info
65
+ update_app_info
66
+ fetch_app_info
67
+ update_release_info
68
+ end
69
+
70
+ %w[binary icon].each do |word|
71
+ define_method("upload_app_#{word}") do
72
+ upload_file(word)
73
+ storage = ENV['SOTRAGE_NAME'] || 'qiniu'
74
+ post("#{fir_api[:base_url]}/auth/#{storage}/callback", send("#{word}_information"))
75
+ end
76
+ end
77
+
78
+ def upload_file(postfix)
79
+ logger.info "Uploading app #{postfix}......"
80
+ url = @uploading_info[:cert][postfix.to_sym][:upload_url]
81
+ info = send("uploading_#{postfix}_info")
82
+ logger.debug "url = #{url}, info = #{info}"
83
+ uploaded_info = post(url, info.merge(manual_callback: true),
84
+ params_to_json: false,
85
+ header: nil)
86
+ rescue StandardError
87
+ logger.error "Uploading app #{postfix} failed"
88
+ exit 1
89
+ end
90
+
91
+ def uploading_icon_info
92
+ large_icon_path = @app_info[:icons].max_by { |f| File.size(f) }
93
+ @uncrushed_icon_path = convert_icon(large_icon_path)
94
+ {
95
+ key: @icon_cert[:key],
96
+ token: @icon_cert[:token],
97
+ file: File.new(@uncrushed_icon_path, 'rb'),
98
+ 'x:is_converted' => '1'
99
+ }
100
+ end
101
+
102
+ def icon_information
103
+ {
104
+ key: @icon_cert[:key],
105
+ token: @icon_cert[:token],
106
+ origin: 'fir-cli',
107
+ parent_id: @app_id,
108
+ fsize: File.size(@uncrushed_icon_path),
109
+ fname: 'blob'
110
+ }
111
+ end
112
+
113
+ def binary_information
114
+ {
115
+ build: @app_info[:build],
116
+ fname: File.basename(@file_path),
117
+ key: @binary_cert[:key],
118
+ name: @app_info[:display_name] || @app_info[:name],
119
+ origin: 'fir-cli',
120
+ parent_id: @app_id,
121
+ release_tag: 'develop',
122
+ fsize: File.size(@file_path),
123
+ release_type: @app_info[:release_type],
124
+ distribution_name: @app_info[:distribution_name],
125
+ token: @binary_cert[:token],
126
+ version: @app_info[:version],
127
+ changelog: @changelog,
128
+ user_id: @user_info[:id]
129
+ }.reject { |x| x.nil? || x == '' }
130
+ end
131
+
132
+ def uploading_binary_info
133
+ {
134
+ key: @binary_cert[:key],
135
+ token: @binary_cert[:token],
136
+ file: File.new(@file_path, 'rb'),
137
+ # Custom variables
138
+ 'x:name' => @app_info[:display_name] || @app_info[:name],
139
+ 'x:build' => @app_info[:build],
140
+ 'x:version' => @app_info[:version],
141
+ 'x:changelog' => @changelog,
142
+ 'x:release_type' => @app_info[:release_type],
143
+ 'x:distribution_name' => @app_info[:distribution_name]
144
+ }
145
+ end
146
+
147
+ def upload_device_info
148
+ return if @app_info[:devices].blank?
149
+
150
+ logger.info 'Updating devices info......'
151
+
152
+ post fir_api[:udids_url], key: @binary_cert[:key],
153
+ udids: @app_info[:devices].join(','),
154
+ api_token: @token
155
+ end
156
+
157
+ def update_app_info
158
+ update_info = { short: @short, passwd: @passwd, is_opened: @is_opened }.compact
159
+
160
+ return if update_info.blank?
161
+
162
+ logger.info 'Updating app info......'
163
+
164
+ patch fir_api[:app_url] + "/#{@app_id}", update_info.merge(api_token: @token)
165
+ end
166
+
167
+ def fetch_uploading_info
168
+ logger.info "Fetching #{@app_info[:identifier]}@fir.im uploading info......"
169
+ logger.info "Uploading app: #{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
170
+
171
+ post fir_api[:app_url], type: @app_info[:type],
172
+ bundle_id: @app_info[:identifier],
173
+ manual_callback: true,
174
+ api_token: @token
175
+ end
176
+
177
+ def fetch_release_id
178
+ get "#{fir_api[:base_url]}/apps/#{@app_id}/releases/find_release_by_key", api_token: @token, key: @binary_cert[:key]
179
+ end
180
+
181
+ def fetch_app_info
182
+ logger.info 'Fetch app info from fir.im'
183
+
184
+ @fir_app_info = get(fir_api[:app_url] + "/#{@app_id}", api_token: @token)
185
+ write_app_info(id: @fir_app_info[:id], short: @fir_app_info[:short], name: @fir_app_info[:name])
186
+ @fir_app_info
187
+ end
188
+
189
+
190
+ def fetch_release_info
191
+ logger.info 'Fetch release info from fir.im'
192
+
193
+ release_info = get(fir_api[:app_url] + "/#{@app_id}" + "/releases", api_token: @token, page: 1)
194
+ if release_info[:datas].count > 0
195
+ @release_id = release_info[:datas][0][:id]
196
+ end
197
+
198
+ end
199
+
200
+ def update_release_info
201
+ logger.info "Update release" ":#{@release_id}" + " info from fir.im"
202
+
203
+ if nil != @release_id
204
+ patch fir_api[:app_url] + "/#{@app_id}" + "/releases" + "/#{@release_id}", api_token: @token, is_history: true
205
+ end
206
+ end
207
+
208
+ def upload_mapping_file_with_publish(options)
209
+ return if !options[:mappingfile] || !options[:proj]
210
+
211
+ logger_info_blank_line
212
+
213
+ mapping options[:mappingfile], proj: options[:proj],
214
+ build: @app_info[:build],
215
+ version: @app_info[:version],
216
+ token: @token
217
+ end
218
+
219
+ def logger_info_app_short_and_qrcode(options)
220
+ @download_url = "#{fir_api[:domain]}/#{@fir_app_info[:short]}"
221
+ @download_url += "?release_id=#{@app_uploaded_callback_data[:release_id]}" if !!options[:need_release_id]
222
+
223
+ logger.info "Published succeed: #{@download_url}"
224
+
225
+ @qrcode_path = "#{File.dirname(@file_path)}/fir-#{@app_info[:name]}.png"
226
+ FIR.generate_rqrcode(@download_url, @qrcode_path)
227
+
228
+ logger.info "Local qrcode file: #{@qrcode_path}" if @export_qrcode
229
+ end
230
+
231
+ private
232
+
233
+ def clean_files
234
+ File.delete(@qrcode_path) unless @export_qrcode
235
+ end
236
+
237
+ def dingtalk_notifier(options)
238
+ if options[:dingtalk_access_token]
239
+ title = "#{@app_info[:name]}-#{@app_info[:version]}(Build #{@app_info[:build]})"
240
+ payload = {
241
+ "msgtype": 'markdown',
242
+ "markdown": {
243
+ "title": "#{title} uploaded",
244
+ "text": "#{title} uploaded at #{Time.now}\nurl: #{@download_url}\n ![app二维码](data:image/png;base64,#{Base64.strict_encode64(File.read(open(@qrcode_path)))})"
245
+ }
246
+ }
247
+ url = "https://oapi.dingtalk.com/robot/send?access_token=#{options[:dingtalk_access_token]}"
248
+ DefaultRest.post(url, payload)
249
+ end
250
+ rescue StandardError => e
251
+ logger.warn "Dingtalk send error #{e.message}"
252
+ end
253
+
254
+ def initialize_publish_options(args, options)
255
+ @file_path = File.absolute_path(args.first.to_s)
256
+ @file_type = File.extname(@file_path).delete('.')
257
+ @token = options[:token] || current_token
258
+ @changelog = read_changelog(options[:changelog]).to_s.to_utf8
259
+ @short = options[:short].to_s
260
+ @passwd = options[:password].to_s
261
+ @is_opened = @passwd.blank? ? options[:open] : false
262
+ @export_qrcode = !!options[:qrcode]
263
+ end
264
+
265
+ def read_changelog(changelog)
266
+ return if changelog.blank?
267
+
268
+ File.exist?(changelog) ? File.read(changelog) : changelog
269
+ end
270
+
271
+ def check_supported_file_and_token
272
+ check_file_exist(@file_path)
273
+ check_supported_file(@file_path)
274
+ check_token_cannot_be_blank(@token)
275
+ fetch_user_info(@token)
276
+ end
277
+
278
+ def convert_icon(origin_path)
279
+ # 兼容性不太好, 蔽掉转化图标
280
+ return origin_path
281
+
282
+ logger.info "Converting app's icon......"
283
+
284
+ if @app_info[:type] == 'ios'
285
+ output_path = Tempfile.new(['uncrushed_icon', '.png']).path
286
+ FIR::Parser::Pngcrush.uncrush_icon(origin_path, output_path)
287
+ origin_path = output_path if File.size(output_path) != 0
288
+ end
289
+
290
+ origin_path
291
+ end
292
+ end
293
+ end
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ module FIR
4
+ VERSION = '1.0.4'
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 "$@"
@@ -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
@@ -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